[
  {
    "path": ".amethyst.sample.yml",
    "content": "# Default settings for Amethyst\n# Repo: `https://github.com/ianyh/Amethyst`\n#\n# Note due to issue 1419 (https://github.com/ianyh/Amethyst/issues/1419) some\n# config values may conflict and not work if they are the same as the default\n# values for Amethyst. You can see these values on GitHub at\n# https://github.com/ianyh/Amethyst/blob/development/Amethyst/default.amethyst\n# If you're experiencing conflicts and the settings are the same as the default,\n# comment out the commands in this file.\n#\n# Move this file to: `~/.amethyst.yml`\n# In order to register changes restart Amethyst.\n# If you experience issues pulling in the changes you can also quit Amethyst and run: `defaults delete com.amethyst.Amethyst.plist`\n# This removes the current preferences and causes Amethyst to restart with default preferences and pull configs from this file.\n\n# layouts - Ordered list of layouts to use by layout key (default tall, wide, fullscreen, and column).\nlayouts:\n  - tall\n  - fullscreen\n  # - tall-right\n  - wide\n  # - two-pane\n  # - middle-wide\n  # - 3column-left\n  # - middle-wide # The legacy name of \"3column-middle\"\n  # - 3column-right\n  # - 4column-left\n  # - 4column-right\n  - column\n  # - row\n  # - floating\n  # - widescreen-tall\n  # - widescreen-tall-right\n  # - bsp\n\n# First mod (default option + shift).\nmod1:\n  - option\n  - shift\n  # - control\n  # - command\n\n# Second mod (default option + shift + control).\nmod2:\n  - option\n  - shift\n  - control\n  # - command\n\n# Commands:\n# special key values\n# space\n# enter\n# up\n# right\n# down\n# left\n\n# special characters require quotes\n# '.'\n# ','\n\n# Move to the next layout in the list.\ncycle-layout:\n  mod: mod1\n  key: space\n\n# Move to the previous layout in the list.\ncycle-layout-backward:\n  mod: mod2\n  key: space\n\n# Shrink the main pane by a percentage of the screen dimension as defined by window-resize-step. Note that not all layouts respond to this command.\nshrink-main:\n  mod: mod1\n  key: h\n\n# Expand the main pane by a percentage of the screen dimension as defined by window-resize-step. Note that not all layouts respond to this command.\nexpand-main:\n  mod: mod1\n  key: l\n\n# Increase the number of windows in the main pane. Note that not all layouts respond to this command.\nincrease-main:\n  mod: mod1\n  key: ','\n\n# Decrease the number of windows in the main pane. Note that not all layouts respond to this command.\ndecrease-main:\n  mod: mod1\n  key: '.'\n\n# General purpose command for custom layouts. Functionality is layout-dependent.\n# command1:\n#   mod: <NONE>\n#   key: <NONE>\n# General purpose command for custom layouts. Functionality is layout-dependent.\n# command2:\n#   mod: <NONE>\n#   key: <NONE>\n# General purpose command for custom layouts. Functionality is layout-dependent.\n# command3:\n#   mod: <NONE>\n#   key: <NONE>\n# General purpose command for custom layouts. Functionality is layout-dependent.\n# command4:\n#   mod: <NONE>\n#   key: <NONE>\n\n# Focus the next window in the list going counter-clockwise.\nfocus-ccw:\n  mod: mod1\n  key: j\n\n# Focus the next window in the list going clockwise.\nfocus-cw:\n  mod: mod1\n  key: k\n\n# Focus the main window in the list.\nfocus-main:\n  mod: mod1\n  key: m\n\n# Focus the next screen in the list going counter-clockwise.\nfocus-screen-ccw:\n  mod: mod1\n  key: p\n\n# Focus the next screen in the list going clockwise.\nfocus-screen-cw:\n  mod: mod1\n  key: n\n\n# Move the currently focused window onto the next screen in the list going counter-clockwise.\nswap-screen-ccw:\n  mod: mod2\n  key: h\n\n# Move the currently focused window onto the next screen in the list going clockwise.\nswap-screen-cw:\n  mod: mod2\n  key: l\n\n# Swap the position of the currently focused window with the next window in the list going counter-clockwise.\nswap-ccw:\n  mod: mod2\n  key: j\n\n# Swap the position of the currently focused window with the next window in the list going clockwise.\nswap-cw:\n  mod: mod2\n  key: k\n\n# Swap the position of the currently focused window with the main window in the list.\nswap-main:\n  mod: mod1\n  key: enter\n\n# Move focus to the n-th screen in the list; e.g., focus-screen-3 will move mouse focus to the 3rd screen. Note that the main window in the given screen will be focused.\n#focus-screen-n:\n# focus-screen-<screen-number>:\n#   mod: mod1\n#   key: y\n# Move the currently focused window to the n-th screen; e.g., throw-screen-3 will move the window to the 3rd screen.\n# throw-screen-n:\n# throw-screen-<screen-number>:\n#   mod: mod1\n#   key: u\n# Move the currently focused window to the n-th space; e.g., throw-space-3 will move the window to the 3rd space.\n# throw-space-<screen-number>:\n#   mod: mod1\n#   key: i\n\n# Select tall layout\nselect-tall-layout:\n  mod: mod1\n  key: a\n\n# Select wide layout\nselect-wide-layout:\n  mod: mod1\n  key: s\n\n# Select fullscreen layout\nselect-fullscreen-layout:\n  mod: mod1\n  key: d\n\n# Select column layout\nselect-column-layout:\n  mod: mod1\n  key: f\n\n# Move the currently focused window to the space to the left.\nthrow-space-left:\n  mod: mod2\n  key: left\n\n# Move currently the focused window to the space to the right.\nthrow-space-right:\n  mod: mod2\n  key: right\n\n# Toggle the floating state of the currently focused window; i.e., if it was floating make it tiled and if it was tiled make it floating.\ntoggle-float:\n  mod: mod1\n  key: t\n\n# Display the layout HUD with the current layout on each screen.\ndisplay-current-layout:\n  mod: mod1\n  key: i\n\n# Turn on or off tiling entirely.\ntoggle-tiling:\n  mod: mod2\n  key: t\n\n# Turn on tiling.\n# enable-tiling:\n#   mod: mod2\n#   key: <NONE>\n\n# Turn off tiling.\n# disable-tiling:\n#   mod: mod2\n#   key: <NONE>\n\n# Rerun the current layout's algorithm.\nreevaluate-windows:\n  mod: mod1\n  key: z\n\n# Turn on or off focus-follows-mouse.\ntoggle-focus-follows-mouse:\n  mod: mod2\n  key: x\n\n# Automatically quit and reopen Amethyst.\nrelaunch-amethyst:\n  mod: mod2\n  key: z\n\n# disable screen padding on builtin display\ndisable-padding-on-builtin-display: false\n\n# Boolean flag for whether or not to add margins between windows (default false).\nwindow-margins: false\n\n# Boolean flag for whether or not to set window margins if there is only one window on the screen, assuming window margins are enabled (default false).\nsmart-window-margins: false\n\n# # Add 10px margin between windows\n# window-margins: true\n# window-margin-size: 5\n# The size of the margins between windows (in px, default 0).\nwindow-margin-size: 0\n\n# The max number of windows that may be visible on a screen at one time before\n# additional windows are minimized. A value of 0 disables the feature.\nwindow-max-count: 0\n\n# The smallest height that a window can be sized to regardless of its layout frame (in px, default 0).\nwindow-minimum-height: 0\n\n# The smallest width that a window can be sized to regardless of its layout frame (in px, default 0)\nwindow-minimum-width: 0\n\n# List of bundle identifiers for applications to either be automatically floating or automatically tiled based on floating-is-blacklist (default []).\nfloating: []\n\n# Boolean flag determining behavior of the floating list. true if the applications should be floating and all others tiled. false if the applications should be tiled and all others floating (default true).\nfloating-is-blacklist: true\n\n# true if screen frames should exclude the status bar. false if the screen frames should include the status bar (default false).\nignore-menu-bar: false\n\n# true if menu bar icon should be hidden (default false).\nhide-menu-bar-icon: false\n\n# true if windows smaller than the small-window-size threshold should be floating by default (default true)\nfloat-small-windows: true\n\n# Pixel threshold for float-small-windows. Windows with both width and height below this value are considered small (in px, default 500).\nsmall-window-size: 500\n\n# true if the mouse should move position to the center of a window when it becomes focused (default false). Note that this is largely incompatible with focus-follows-mouse.\nmouse-follows-focus: false\n\n# true if the windows underneath the mouse should become focused as the mouse moves (default false). Note that this is largely incompatible with mouse-follows-focus\nfocus-follows-mouse: false\n\n# true if dragging and dropping windows on to each other should swap their positions (default false).\nmouse-swaps-windows: false\n\n# true if changing the frame of a window with the mouse should update the layout to accommodate the change (default false). Note that not all layouts will be able to respond to the change.\nmouse-resizes-windows: false\n\n# true to display the name of the layout when a new layout is selected (default true).\nenables-layout-hud: true\n\n# true to display the name of the layout when moving to a new space (default true).\nenables-layout-hud-on-space-change: true\n\n# true to get updates to beta versions of the software (default false).\nuse-canary-build: false\n\n# true to insert new windows into the first position and false to insert new windows into the last position (default false).\nnew-windows-to-main: false\n\n# true to automatically move to a space when throwing a window to it (default true).\nfollow-space-thrown-windows: true\n\n# The integer percentage of the screen dimension to increment and decrement main pane ratios by (default 5).\nwindow-resize-step: 5\n\n# Padding to apply between windows and the left edge of the screen (in px, default 0).\nscreen-padding-left: 0\n\n# Padding to apply between windows and the right edge of the screen (in px, default 0).\nscreen-padding-right: 0\n\n# Padding to apply between windows and the top edge of the screen (in px, default 0).\nscreen-padding-top: 0\n\n# Padding to apply between windows and the bottom edge of the screen (in px, default 0).\nscreen-padding-bottom: 0\n\n# true to maintain layout state across application executions (default true).\nrestore-layouts-on-launch: true\n\n# true to display some optional debug information in the layout HUD (default false).\ndebug-layout-info: false\n"
  },
  {
    "path": ".eslintrc.yml",
    "content": "---\n    env:\n        es6: true\n    parserOptions:\n        ecmaVersion: 9\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**Applications:**\nWhat applications are involved?\n\n**To Reproduce**\nSteps to reproduce the behavior:\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Versions:**\n - macOS: \n - Amethyst: \n \n**Debug Info**\n```\n$ /Applications/Amethyst.app/Contents/MacOS/Amethyst --debug-info [--include-apps]\n```\nNote: `--include-apps` will list your manageable applications, but is optional if you don't want to list that.\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/tests.yml",
    "content": "name: Tests\n\non:\n  push:\n    branches: [ development ]\n  pull_request:\n\njobs:\n  build:\n    name: Build and run unit tests\n    runs-on: macos-latest\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v2\n      - name: Dependencies\n        run: |\n          brew bundle\n      - name: Test\n        run: |\n          set -o pipefail && xcodebuild -workspace Amethyst.xcworkspace -scheme Amethyst clean test | xcbeautify\n\n"
  },
  {
    "path": ".gitignore",
    "content": "# Xcode\n.DS_Store\n*/build/*\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\nprofile\n*.moved-aside\nDerivedData\n.idea/\n*.hmap\nPods\nbuild\ncrashlytics_api_key\ncrashlytics_app_key\nCarthage\nAMKeys.h\nfastlane/report.xml\n*profraw\n\n# Homebrew\nBrewfile.lock.json\n\n"
  },
  {
    "path": ".hound.yml",
    "content": "swiftlint:\n    config_file: .swiftlint.yml\n\neslint:\n    enabled: true\n    config_file: .eslintrc.yml\n"
  },
  {
    "path": ".swiftlint.yml",
    "content": "disabled_rules:\n    - function_body_length\n    - closing_brace\n    - statement_position\n    - force_cast\n    - force_try\n    - no_space_in_method_call\n    - file_length\n    - type_body_length\nincluded:\n    - Amethyst\n    - AmethystTests\nline_length:\n    warning: 200\n    ignores_comments: true\ncyclomatic_complexity: 15\nlarge_tuple: 3\nnesting:\n    type_level: 2\nidentifier_name:\n    excluded:\n        - id\n"
  },
  {
    "path": "Amethyst/Amethyst-Bridging-Header.h",
    "content": ""
  },
  {
    "path": "Amethyst/Amethyst-Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>${EXECUTABLE_NAME}</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>${PRODUCT_NAME}</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSApplicationCategoryType</key>\n\t<string>public.app-category.utilities</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>${MACOSX_DEPLOYMENT_TARGET}</string>\n\t<key>LSUIElement</key>\n\t<true/>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n\t<key>SUCanaryFeedURL</key>\n\t<string>https://ianyh.com/amethyst/canary-appcast.xml</string>\n\t<key>SUFeedURL</key>\n\t<string>https://ianyh.com/amethyst/appcast.xml</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "Amethyst/Amethyst.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "Amethyst/AmethystDebug.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.cs.disable-library-validation</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Amethyst/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport CoreServices\nimport Foundation\nimport LoginServiceKit\nimport RxCocoa\nimport RxSwift\nimport Silica\nimport Sparkle\nimport SwiftyBeaver\n\nclass AppDelegate: NSObject, NSApplicationDelegate {\n    static let windowManagerEncodingKey = \"EncodedWindowManager\"\n\n    @IBOutlet var preferencesWindowController: PreferencesWindowController?\n\n    fileprivate var windowManager: WindowManager<SIApplication>?\n    private var hotKeyManager: HotKeyManager<SIApplication>?\n\n    fileprivate var statusItem: NSStatusItem?\n    @IBOutlet var statusItemMenu: NSMenu?\n    @IBOutlet var versionMenuItem: NSMenuItem?\n    @IBOutlet var startAtLoginMenuItem: NSMenuItem?\n    @IBOutlet var toggleGlobalTilingMenuItem: NSMenuItem?\n    @IBOutlet var layoutsMenuItem: NSMenuItem?\n\n    private var isFirstLaunch = true\n\n    func applicationDidFinishLaunching(_ notification: Notification) {\n        #if DEBUG\n            log.addDestination(ConsoleDestination())\n        #endif\n\n        if CommandLine.arguments.contains(\"--log\") {\n            let destination = ConsoleDestination()\n            destination.useNSLog = true\n            log.addDestination(destination)\n        }\n\n        log.info(\"Logging is enabled\")\n        log.debug(\"Debug logging is enabled\")\n\n        UserConfiguration.shared.delegate = self\n        UserConfiguration.shared.load()\n\n        #if RELEASE\n            let appcastURLString = { () -> String? in\n                if UserConfiguration.shared.useCanaryBuild() {\n                    return Bundle.main.infoDictionary?[\"SUCanaryFeedURL\"] as? String\n                } else {\n                    return Bundle.main.infoDictionary?[\"SUFeedURL\"] as? String\n                }\n            }()!\n\n            SUUpdater.shared().feedURL = URL(string: appcastURLString)\n        #endif\n\n        preferencesWindowController?.window?.level = .floating\n\n        if let encodedWindowManager = UserDefaults.standard.data(forKey: AppDelegate.windowManagerEncodingKey), UserConfiguration.shared.restoreLayoutsOnLaunch() {\n            let decoder = JSONDecoder()\n            windowManager = try? decoder.decode(WindowManager<SIApplication>.self, from: encodedWindowManager)\n        }\n\n        windowManager = windowManager ?? WindowManager(userConfiguration: UserConfiguration.shared)\n        hotKeyManager = HotKeyManager(userConfiguration: UserConfiguration.shared)\n\n        hotKeyManager?.setUpWithWindowManager(windowManager!, configuration: UserConfiguration.shared, appDelegate: self)\n    }\n\n    override func awakeFromNib() {\n        super.awakeFromNib()\n\n        let version = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as! String\n        let shortVersion = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as! String\n        let statusItemImage = NSImage(named: \"icon-statusitem\")\n        statusItemImage?.isTemplate = true\n\n        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)\n        statusItem?.image = statusItemImage\n        statusItem?.menu = statusItemMenu\n        statusItem?.highlightMode = true\n\n        let hideMenuBarIcon: Bool = UserConfiguration.shared.hideMenuBarIcon()\n        statusItem?.isVisible = !hideMenuBarIcon\n\n        versionMenuItem?.title = \"Version \\(shortVersion) (\\(version))\"\n        toggleGlobalTilingMenuItem?.title = \"Disable\"\n\n        startAtLoginMenuItem?.state = (LoginServiceKit.isExistLoginItems(at: Bundle.main.bundlePath) ? .on : .off)\n\n        // Set up status item menu delegate to refresh layouts when main menu is opened\n        statusItemMenu?.delegate = self\n    }\n\n    func applicationDidBecomeActive(_ notification: Notification) {\n        guard !isFirstLaunch else {\n            isFirstLaunch = false\n            return\n        }\n\n        showPreferencesWindow(self)\n    }\n\n    func applicationWillTerminate(_ notification: Notification) {\n        guard let windowManager = windowManager else {\n            return\n        }\n\n        do {\n            let encoder = JSONEncoder()\n            let encodedWindowManager = try encoder.encode(windowManager)\n            UserDefaults.standard.set(encodedWindowManager, forKey: AppDelegate.windowManagerEncodingKey)\n        } catch {\n            log.error(\"Failed to encode window manager: \\(error)\")\n        }\n    }\n\n    @IBAction func toggleStartAtLogin(_ sender: AnyObject) {\n        if startAtLoginMenuItem?.state == .off {\n            LoginServiceKit.addLoginItems(at: Bundle.main.bundlePath)\n        } else {\n            LoginServiceKit.removeLoginItems(at: Bundle.main.bundlePath)\n        }\n        startAtLoginMenuItem?.state = (LoginServiceKit.isExistLoginItems(at: Bundle.main.bundlePath) ? .on : .off)\n    }\n\n    @IBAction func toggleGlobalTiling(_ sender: AnyObject) {\n        UserConfiguration.shared.tilingEnabled = !UserConfiguration.shared.tilingEnabled\n        windowManager?.markAllScreensForReflow()\n    }\n\n    @IBAction func resetLayouts(_ sender: AnyObject) {\n        UserDefaults.standard.removeObject(forKey: AppDelegate.windowManagerEncodingKey)\n        windowManager?.reset()\n    }\n\n    @IBAction func relaunch(_ sender: AnyObject) {\n        AppManager.relaunch()\n    }\n\n    @IBAction func showPreferencesWindow(_ sender: AnyObject) {\n        guard let isVisible = preferencesWindowController?.window?.isVisible, !isVisible else {\n            return\n        }\n\n        preferencesWindowController?.showWindow(nil)\n        NSApp.activate(ignoringOtherApps: true)\n\n        presentDotfileWarningIfNecessary()\n    }\n\n    @IBAction func checkForUpdates(_ sender: AnyObject) {\n        #if RELEASE\n            SUUpdater.shared().checkForUpdates(sender)\n        #endif\n    }\n\n    private func presentDotfileWarningIfNecessary() {\n        let shouldWarn = !UserDefaults.standard.bool(forKey: \"disable-dotfile-conflict-warning\")\n        if shouldWarn && UserConfiguration.shared.hasCustomConfiguration() {\n            let alert = NSAlert()\n            alert.alertStyle = .warning\n            alert.messageText = \"Warning\"\n            alert.informativeText = \"You have a .amethyst file, which can override in-app preferences. You may encounter unexpected behavior.\"\n            alert.showsSuppressionButton = true\n            alert.runModal()\n\n            if alert.suppressionButton?.state == .on {\n                UserDefaults.standard.set(true, forKey: \"disable-dotfile-conflict-warning\")\n            }\n        }\n    }\n\n    private func populateLayoutsMenu() {\n        guard let layoutsMenuItem = layoutsMenuItem,\n              let submenu = layoutsMenuItem.submenu else {\n            return\n        }\n\n        // Clear existing items\n        submenu.removeAllItems()\n\n        // Get screen manager: try focused screen first, then screen under mouse cursor\n        let screenManager: ScreenManager<WindowManager<SIApplication>>? = {\n            if let focused = windowManager?.focusedScreenManager() {\n                return focused\n            }\n            // Fallback to screen containing mouse cursor (useful when clicking menu bar)\n            let mouseLocation = NSEvent.mouseLocation\n            if let nsScreen = NSScreen.screens.first(where: { NSMouseInRect(mouseLocation, $0.frame, false) }) {\n                let amScreen = AMScreen(screen: nsScreen)\n                return windowManager?.screenManager(for: amScreen)\n            }\n            return nil\n        }()\n\n        guard let screenManager = screenManager else {\n            let errorItem = NSMenuItem(title: \"Unable to determine current screen\", action: nil, keyEquivalent: \"\")\n            errorItem.isEnabled = false\n            submenu.addItem(errorItem)\n            return\n        }\n\n        // Get layouts from the screen manager (not from global config)\n        let layouts = screenManager.layoutsInfo\n\n        // Check if no layouts are available and return early\n        if layouts.isEmpty {\n            let noLayoutsItem = NSMenuItem(title: \"No layouts enabled\", action: nil, keyEquivalent: \"\")\n            noLayoutsItem.isEnabled = false\n            submenu.addItem(noLayoutsItem)\n            return\n        }\n\n        // Add menu items for each layout in the screen manager\n        for layoutInfo in layouts {\n            let menuItem = NSMenuItem(title: layoutInfo.name, action: #selector(selectLayout(_:)), keyEquivalent: \"\")\n            menuItem.target = self\n            menuItem.representedObject = layoutInfo.key\n            menuItem.state = layoutInfo.isSelected ? .on : .off\n\n            submenu.addItem(menuItem)\n        }\n    }\n\n    @IBAction func selectLayout(_ sender: NSMenuItem) {\n        guard let layoutKey = sender.representedObject as? String,\n              let windowManager = windowManager,\n              let screenManager = windowManager.focusedScreenManager() else {\n            return\n        }\n\n        screenManager.selectLayout(layoutKey)\n        // Menu will be refreshed automatically when next opened via NSMenuDelegate\n    }\n}\n\nextension AppDelegate: NSWindowDelegate {\n    func windowWillClose(_ notification: Notification) {\n        windowManager?.preferencesDidClose()\n    }\n}\n\nextension AppDelegate: NSMenuDelegate {\n    func menuWillOpen(_ menu: NSMenu) {\n        // Refresh layouts menu when main status item menu is about to open\n        if menu == statusItemMenu {\n            populateLayoutsMenu()\n        }\n    }\n}\n\nextension AppDelegate: UserConfigurationDelegate {\n    func configurationGlobalTilingDidChange(_ userConfiguration: UserConfiguration) {\n        var statusItemImage: NSImage?\n        if UserConfiguration.shared.tilingEnabled == true {\n            statusItemImage = NSImage(named: \"icon-statusitem\")\n            toggleGlobalTilingMenuItem?.title = \"Disable Tiling\"\n        } else {\n            statusItemImage = NSImage(named: \"icon-statusitem-disabled\")\n            toggleGlobalTilingMenuItem?.title = \"Enable Tiling\"\n        }\n        statusItemImage?.isTemplate = true\n        statusItem?.image = statusItemImage\n    }\n\n    func configurationAccessibilityPermissionsDidChange(_ userConfiguration: UserConfiguration) {\n        windowManager?.reevaluateWindows()\n    }\n}\n"
  },
  {
    "path": "Amethyst/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"23727\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"23727\"/>\n        <capability name=\"Image references\" minToolsVersion=\"12.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"494\" id=\"495\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <menu id=\"536\">\n            <items>\n                <menuItem title=\"Version\" enabled=\"NO\" id=\"bNZ-Ry-Q4Q\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                </menuItem>\n                <menuItem isSeparatorItem=\"YES\" id=\"uP9-ch-b7n\"/>\n                <menuItem title=\"Disable Tiling\" id=\"rjJ-w4-5Ht\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"toggleGlobalTiling:\" target=\"494\" id=\"3aM-9U-hWx\"/>\n                    </connections>\n                </menuItem>\n                <menuItem title=\"Layouts\" id=\"layouts-menu-item\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" id=\"layouts-submenu\">\n                        <items/>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Start Amethyst on Login\" id=\"549\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"toggleStartAtLogin:\" target=\"494\" id=\"550\"/>\n                    </connections>\n                </menuItem>\n                <menuItem title=\"Preferences...\" id=\"utO-om-0eu\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"showPreferencesWindow:\" target=\"494\" id=\"DhJ-Jj-71M\"/>\n                    </connections>\n                </menuItem>\n                <menuItem isSeparatorItem=\"YES\" id=\"548\"/>\n                <menuItem title=\"Check for Updates...\" id=\"554\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"checkForUpdates:\" target=\"494\" id=\"l9Y-Ct-Bzq\"/>\n                    </connections>\n                </menuItem>\n                <menuItem title=\"Reset Layouts\" id=\"8DH-wG-R1x\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"resetLayouts:\" target=\"494\" id=\"Osy-WG-Dy8\"/>\n                    </connections>\n                </menuItem>\n                <menuItem title=\"Relaunch Amethyst\" id=\"crd-PN-Vy2\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <connections>\n                        <action selector=\"relaunch:\" target=\"494\" id=\"EeN-oj-Mtv\"/>\n                    </connections>\n                </menuItem>\n                <menuItem isSeparatorItem=\"YES\" id=\"553\"/>\n                <menuItem title=\"Quit Amethyst\" keyEquivalent=\"q\" id=\"545\">\n                    <connections>\n                        <action selector=\"terminate:\" target=\"-3\" id=\"546\"/>\n                    </connections>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"-179\" y=\"92\"/>\n        </menu>\n        <customObject id=\"494\" customClass=\"AppDelegate\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"preferencesWindowController\" destination=\"0fi-bi-huz\" id=\"dfn-JC-QBQ\"/>\n                <outlet property=\"startAtLoginMenuItem\" destination=\"549\" id=\"551\"/>\n                <outlet property=\"statusItemMenu\" destination=\"536\" id=\"547\"/>\n                <outlet property=\"toggleGlobalTilingMenuItem\" destination=\"rjJ-w4-5Ht\" id=\"4Dq-e8-R1M\"/>\n                <outlet property=\"versionMenuItem\" destination=\"bNZ-Ry-Q4Q\" id=\"jXC-pY-N2a\"/>\n                <outlet property=\"layoutsMenuItem\" destination=\"layouts-menu-item\" id=\"layouts-menu-outlet\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"420\" customClass=\"NSFontManager\"/>\n        <window allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" visibleAtLaunch=\"NO\" frameAutosaveName=\"\" animationBehavior=\"default\" toolbarStyle=\"expanded\" id=\"pO4-7U-3HA\" customClass=\"PreferencesWindow\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"446\" y=\"278\" width=\"480\" height=\"270\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"1440\" height=\"875\"/>\n            <view key=\"contentView\" id=\"IHj-fa-gIc\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"480\" height=\"260\"/>\n                <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n            </view>\n            <toolbar key=\"toolbar\" implicitIdentifier=\"2383369F-98A6-4420-8EFE-319DB9588C53\" autosavesConfiguration=\"NO\" allowsUserCustomization=\"NO\" showsBaselineSeparator=\"NO\" displayMode=\"iconAndLabel\" sizeMode=\"regular\" id=\"5oI-m4-akz\">\n                <allowedToolbarItems>\n                    <toolbarItem implicitItemIdentifier=\"D4FDE95C-2F3E-4C93-8777-9FBAD1EDB613\" explicitItemIdentifier=\"general\" label=\"General\" paletteLabel=\"General\" selectable=\"YES\" id=\"WV2-G9-AL4\">\n                        <imageReference key=\"image\" image=\"text.and.command.macwindow\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"bq3-bL-T4h\"/>\n                        </connections>\n                    </toolbarItem>\n                    <toolbarItem implicitItemIdentifier=\"C7D5B660-7047-4353-8F39-18609BCF05D1\" explicitItemIdentifier=\"layouts\" label=\"Layouts\" paletteLabel=\"Layouts\" selectable=\"YES\" id=\"FsX-hN-WZb\" userLabel=\"Layouts\">\n                        <imageReference key=\"image\" image=\"uiwindow.split.2x1\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"mBI-Du-M23\"/>\n                        </connections>\n                    </toolbarItem>\n                    <toolbarItem implicitItemIdentifier=\"04C17AAF-5F6B-422F-8592-E8AA78DEB9C7\" explicitItemIdentifier=\"shortcuts\" label=\"Shortcuts\" paletteLabel=\"Shortcuts\" tag=\"1\" selectable=\"YES\" id=\"j0a-87-Yvz\">\n                        <imageReference key=\"image\" image=\"123.rectangle\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"9kO-ms-dfq\"/>\n                        </connections>\n                    </toolbarItem>\n                    <toolbarItem implicitItemIdentifier=\"74318EC6-A099-4964-B13B-6B2B699C7EC3\" explicitItemIdentifier=\"mouse\" label=\"Mouse\" paletteLabel=\"Mouse\" selectable=\"YES\" id=\"dfc-D6-FsG\" userLabel=\"Mouse\">\n                        <imageReference key=\"image\" image=\"dots.and.line.vertical.and.cursorarrow.rectangle\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"0vo-hl-zVN\"/>\n                        </connections>\n                    </toolbarItem>\n                    <toolbarItem implicitItemIdentifier=\"A7FE5E71-A3DC-43B8-92B2-5CF107F2DB2F\" explicitItemIdentifier=\"floating\" label=\"Floating\" paletteLabel=\"Floating\" tag=\"-1\" selectable=\"YES\" id=\"QEp-pN-Lz9\">\n                        <imageReference key=\"image\" image=\"macwindow.on.rectangle\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"WEg-vF-5FE\"/>\n                        </connections>\n                    </toolbarItem>\n                    <toolbarItem implicitItemIdentifier=\"319E6DB9-3D79-40BF-95A2-B84B5236FF8B\" explicitItemIdentifier=\"debug\" label=\"Debug\" paletteLabel=\"Debug\" tag=\"-1\" selectable=\"YES\" id=\"KUo-2v-ywG\">\n                        <imageReference key=\"image\" image=\"waveform.path.ecg.rectangle\" catalog=\"system\" symbolScale=\"large\"/>\n                        <size key=\"minSize\" width=\"22\" height=\"22\"/>\n                        <size key=\"maxSize\" width=\"22\" height=\"22\"/>\n                        <connections>\n                            <action selector=\"selectPane:\" target=\"0fi-bi-huz\" id=\"aNs-0B-bfi\"/>\n                        </connections>\n                    </toolbarItem>\n                </allowedToolbarItems>\n                <defaultToolbarItems>\n                    <toolbarItem reference=\"WV2-G9-AL4\"/>\n                    <toolbarItem reference=\"FsX-hN-WZb\"/>\n                    <toolbarItem reference=\"j0a-87-Yvz\"/>\n                    <toolbarItem reference=\"dfc-D6-FsG\"/>\n                    <toolbarItem reference=\"QEp-pN-Lz9\"/>\n                    <toolbarItem reference=\"KUo-2v-ywG\"/>\n                </defaultToolbarItems>\n                <connections>\n                    <outlet property=\"delegate\" destination=\"0fi-bi-huz\" id=\"zer-Uh-aco\"/>\n                </connections>\n            </toolbar>\n            <connections>\n                <outlet property=\"closeMenuItem\" destination=\"qQS-B4-nPy\" id=\"4g6-N2-DxV\"/>\n                <outlet property=\"delegate\" destination=\"494\" id=\"OGF-zj-QHD\"/>\n            </connections>\n            <point key=\"canvasLocation\" x=\"606\" y=\"152\"/>\n        </window>\n        <customObject id=\"0fi-bi-huz\" userLabel=\"Preferences Window Controller\" customClass=\"PreferencesWindowController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"window\" destination=\"pO4-7U-3HA\" id=\"9gm-N1-3ZQ\"/>\n            </connections>\n        </customObject>\n        <menu id=\"zK2-oi-EfJ\">\n            <items>\n                <menuItem title=\"Close\" keyEquivalent=\"w\" id=\"qQS-B4-nPy\"/>\n            </items>\n            <point key=\"canvasLocation\" x=\"560\" y=\"-126\"/>\n        </menu>\n    </objects>\n    <resources>\n        <image name=\"123.rectangle\" catalog=\"system\" width=\"24\" height=\"18\"/>\n        <image name=\"dots.and.line.vertical.and.cursorarrow.rectangle\" catalog=\"system\" width=\"24\" height=\"25\"/>\n        <image name=\"macwindow.on.rectangle\" catalog=\"system\" width=\"25\" height=\"20\"/>\n        <image name=\"text.and.command.macwindow\" catalog=\"system\" width=\"24\" height=\"18\"/>\n        <image name=\"uiwindow.split.2x1\" catalog=\"system\" width=\"24\" height=\"18\"/>\n        <image name=\"waveform.path.ecg.rectangle\" catalog=\"system\" width=\"24\" height=\"18\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Amethyst/Categories/NSRunningApplication+Manageable.swift",
    "content": "//\n//  NSRunningApplication+Manageable.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\nimport Foundation\n\nenum Manageable {\n    case manageable\n    case unmanageable\n    case undetermined\n}\n\nprivate let ignoredBundleIDs = Set([\n    \"com.apple.dashboard\",\n    \"com.apple.loginwindow\",\n    \"com.apple.notificationcenterui\",\n    \"com.apple.wifi.WiFiAgent\",\n    \"com.apple.Spotlight\",\n    \"com.apple.systemuiserver\",\n    \"com.apple.dock\",\n    \"com.apple.AirPlayUIAgent\",\n    \"com.apple.dock.extra\",\n    \"com.apple.PowerChime\",\n    \"com.apple.WebKit.Networking\",\n    \"com.apple.WebKit.WebContent\",\n    \"com.apple.WebKit.GPU\",\n    \"com.apple.FollowUpUI\",\n    \"com.apple.controlcenter\",\n    \"com.apple.SoftwareUpdateNotificationManager\",\n    \"com.apple.TextInputMenuAgent\",\n    \"com.apple.TextInputSwitcher\",\n    \"com.apple.WindowManager\",\n    \"com.apple.accessibility.AXVisualSupportAgent\",\n    \"com.apple.talagent\",\n    \"com.apple.wallpaper.agent\",\n    \"com.apple.CharacterPaletteIM\",\n    \"com.apple.LocalAuthentication.UIAgent\",\n    \"com.apple.security.Keychain-Circle-Notification\",\n    \"com.apple.backgroundtaskmanagement.agent\",\n    \"com.apple.CoreLocationAgent\",\n    \"com.apple.OSDUIHelper\",\n    \"com.apple.ViewBridgeAuxiliary\"\n])\n\nprotocol BundleIdentifiable {\n    var bundleIdentifier: String? { get }\n}\n\nextension NSRunningApplication: BundleIdentifiable {}\n\nextension NSRunningApplication {\n    var isManageable: Manageable {\n        if let bundleIdentifier = bundleIdentifier, ignoredBundleIDs.contains(bundleIdentifier) {\n            return .unmanageable\n        }\n\n        guard isFinishedLaunching else {\n            return .undetermined\n        }\n\n        guard case .regular = activationPolicy else {\n            return .undetermined\n        }\n\n        return .manageable\n    }\n}\n"
  },
  {
    "path": "Amethyst/Categories/NSTableView+Amethyst.swift",
    "content": "//\n//  NSTableView+Amethyst.swift\n//  Amethyst\n//\n//  Created by James Zaghini on 15/5/18.\n//  Copyright © 2018 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\n\nextension NSTableView {\n    static let noRowSelectedIndex = -1\n}\n"
  },
  {
    "path": "Amethyst/Debug/AppsInfo.swift",
    "content": "//\n//  AppsInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/23.\n//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ArgumentParser\nimport Cocoa\nimport Silica\n\nstruct Apps: ParsableCommand {\n    @Flag(help: \"Include unmanaged applications.\")\n    var includeUnmanaged = false\n\n    mutating func run() throws {\n        let applications = NSWorkspace.shared.runningApplications\n        for application in applications where includeUnmanaged || application.isManageable == .manageable {\n            let app = SIApplication(runningApplication: application)\n            print(\"\"\"\n            Title: \\(app.title() ?? \"<no title>\")\n            Bundle Identifier: \\(application.bundleIdentifier ?? \"<no id>\")\n            Activation Policy: \\(application.activationPolicy)\n            pid: \\(app.pid())\n            Manageable: \\(application.isManageable)\n\n            \"\"\")\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Debug/DebugInfo.swift",
    "content": "//\n//  DebugInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/24/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ArgumentParser\nimport Cocoa\n\nstruct Debug: ParsableCommand {\n    static var configuration: CommandConfiguration = CommandConfiguration(\n        abstract: \"Generate diagnostic reports on system state.\",\n        subcommands: [Apps.self, Windows.self],\n        defaultSubcommand: Windows.self\n    )\n}\n\nstruct DebugInfo {\n    static func description(arguments: [String]) -> String {\n        var infos = [\n            \"Version: \\(version())\",\n            \"OS version: \\(ProcessInfo.processInfo.operatingSystemVersionString)\",\n            \"Screens:\\n\\(screens())\",\n            \"Configuration:\\n\\(config())\"\n        ]\n\n        if arguments.contains(\"--include-apps\") {\n            infos.append(\"Manageable applications:\\n\\(applications())\")\n        }\n\n        return infos.joined(separator: \"\\n\\n\")\n    }\n\n    static func version() -> String {\n        let version = Bundle.main.infoDictionary?[\"CFBundleVersion\"] as! String\n        let shortVersion = Bundle.main.infoDictionary?[\"CFBundleShortVersionString\"] as! String\n        return \"\\(shortVersion) (\\(version))\"\n    }\n\n    static func isProcessTrusted() -> Bool {\n        let options = [\n            kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: false\n        ]\n\n        return AXIsProcessTrustedWithOptions(options as CFDictionary)\n    }\n\n    static func screens() -> String {\n        return NSScreen.screens.map { \"\\t\\($0.frame) [\\($0.frameIncludingDockAndMenu())]\" }.joined(separator: \"\\n\")\n    }\n\n    static func applications() -> String {\n        return NSWorkspace.shared.runningApplications\n            .filter { $0.isManageable == .manageable }\n            .map { \"\\t\\($0.localizedName ?? \"<unknown name>\") (\\($0.bundleIdentifier ?? \"<unknown bundle id>\"))\" }\n            .joined(separator: \"\\n\")\n    }\n\n    static func config() -> String {\n        return UserDefaults.standard.dictionaryRepresentation()\n            .filter { ConfigurationKey(rawValue: $0.key) != nil }\n            .map { \"\\($0): \\($1)\" }.joined(separator: \"\\n\")\n    }\n}\n"
  },
  {
    "path": "Amethyst/Debug/ScreensInfo.swift",
    "content": "//\n//  ScreensInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/23.\n//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ArgumentParser\nimport Cocoa\nimport Silica\n\nextension AMScreen {\n    func debugDescription() -> String {\n        return \"\"\"\n        \\tScreenID: \\(screenID()!)\n        \\tScreen Frame: \\(frame())\n        \"\"\"\n    }\n}\n\nstruct Screens: ParsableCommand {\n    mutating func run() throws {\n    }\n}\n"
  },
  {
    "path": "Amethyst/Debug/WindowsInfo.swift",
    "content": "//\n//  WindowsInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/5/23.\n//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ArgumentParser\nimport Cocoa\nimport Foundation\nimport Silica\n\nextension AXWindow {\n    func debugInfo(redactTitles: Bool) -> String {\n        let screenDescription = screen().map { AMScreen(screen: $0).debugDescription() }\n\n        return \"\"\"\n        \\tTitle: \\(redactTitles ? \"<redacted>\" : title() ?? \"<no title>\")\n        \\tFrame: \\(frame())\n        \\tid: \\(windowID())\n        \\(screenDescription ?? \"Screen: unknown\")\n        \\tisActive: \\(isActive())\n        \\tisOnScreen: \\(isOnScreen())\n        \\tisFocused: \\(isFocused())\n        \\tshouldBeManaged: \\(shouldBeManaged())\n        \\tshouldFloat: \\(shouldFloat())\n        \"\"\"\n    }\n}\n\nstruct Windows: ParsableCommand {\n    @Flag(help: \"Include windows of unmanaged applications.\")\n    var includeUnmanaged = false\n\n    @Flag(help: \"Redact window titles.\")\n    var redactWindowTitles = false\n\n    mutating func run() throws {\n        let applications = NSWorkspace.shared.runningApplications\n        for application in applications where includeUnmanaged || application.isManageable == .manageable {\n            let app = SIApplication(runningApplication: application)\n            print(\"\\(app.title() ?? \"<no title>\") (pid \\(app.pid()))\")\n            for window in app.windows() {\n                let axWindow = AXWindow(element: window)!\n                print(axWindow.debugInfo(redactTitles: redactWindowTitles))\n                print(\"\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Events/HotKeyManager.swift",
    "content": "//\n//  HotKeyManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Carbon\nimport Foundation\nimport Silica\n\n// Type for defining key code.\ntypealias AMKeyCode = Int\n\n// Type for defining modifier flags.\ntypealias AMModifierFlags = NSEvent.ModifierFlags\n\n// Specific key code defined to be invalid.\n// Can be used to identify if a returned key code is valid or not.\nprivate let AMKeyCodeInvalid: AMKeyCode = 0xFF\n\ntypealias HotKeyHandler = () -> Void\n\nclass HotKeyManager<Application: ApplicationType>: NSObject {\n    private let userConfiguration: UserConfiguration\n\n    private(set) lazy var stringToKeyCodes: [String: [AMKeyCode]] = {\n        return self.constructKeyCodeMap()\n    }()\n\n    init(userConfiguration: UserConfiguration) {\n        self.userConfiguration = userConfiguration\n        super.init()\n        _ = constructKeyCodeMap()\n    }\n\n    private static func keyCodeForNumber(_ number: NSNumber) -> AMKeyCode {\n        let string = \"\\(number)\"\n\n        guard !string.isEmpty else {\n            return AMKeyCodeInvalid\n        }\n\n        switch string.last! {\n        case \"1\":\n            return kVK_ANSI_1\n        case \"2\":\n            return kVK_ANSI_2\n        case \"3\":\n            return kVK_ANSI_3\n        case \"4\":\n            return kVK_ANSI_4\n        case \"5\":\n            return kVK_ANSI_5\n        case \"6\":\n            return kVK_ANSI_6\n        case \"7\":\n            return kVK_ANSI_7\n        case \"8\":\n            return kVK_ANSI_8\n        case \"9\":\n            return kVK_ANSI_9\n        case \"0\":\n            return kVK_ANSI_0\n        default:\n            return AMKeyCodeInvalid\n        }\n    }\n\n    func setUpWithWindowManager(_ windowManager: WindowManager<Application>, configuration: UserConfiguration, appDelegate: AppDelegate) {\n        constructCommandWithCommandKey(CommandKey.cycleLayoutForward.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.cycleLayoutForward()\n        }\n\n        constructCommandWithCommandKey(CommandKey.cycleLayoutBackward.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.cycleLayoutBackward()\n        }\n\n        constructCommandWithCommandKey(CommandKey.shrinkMain.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let panedLayout = layout as? PanedLayout {\n                    panedLayout.shrinkMainPane()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.expandMain.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let panedLayout = layout as? PanedLayout {\n                    panedLayout.expandMainPane()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.increaseMain.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let panedLayout = layout as? PanedLayout {\n                    panedLayout.increaseMainPaneCount()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.decreaseMain.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let panedLayout = layout as? PanedLayout {\n                    panedLayout.decreaseMainPaneCount()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.command1.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let customLayout = layout as? CustomLayout {\n                    customLayout.command1()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.command2.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let customLayout = layout as? CustomLayout {\n                    customLayout.command2()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.command3.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let customLayout = layout as? CustomLayout {\n                    customLayout.command3()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.command4.rawValue) {\n            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n            screenManager?.updateCurrentLayout { layout in\n                if let customLayout = layout as? CustomLayout {\n                    customLayout.command4()\n                }\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.focusCCW.rawValue) {\n            windowManager.focusTransitionCoordinator.moveFocusCounterClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.focusCW.rawValue) {\n            windowManager.focusTransitionCoordinator.moveFocusClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.focusMain.rawValue) {\n            windowManager.focusTransitionCoordinator.moveFocusToMain()\n        }\n\n        constructCommandWithCommandKey(CommandKey.focusScreenCCW.rawValue) {\n            windowManager.focusTransitionCoordinator.moveFocusScreenCounterClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.focusScreenCW.rawValue) {\n            windowManager.focusTransitionCoordinator.moveFocusScreenClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.swapScreenCCW.rawValue) {\n            windowManager.windowTransitionCoordinator.swapFocusedWindowScreenCounterClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.swapScreenCW.rawValue) {\n            windowManager.windowTransitionCoordinator.swapFocusedWindowScreenClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.swapCCW.rawValue) {\n            windowManager.windowTransitionCoordinator.swapFocusedWindowCounterClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.swapCW.rawValue) {\n            windowManager.windowTransitionCoordinator.swapFocusedWindowClockwise()\n        }\n\n        constructCommandWithCommandKey(CommandKey.swapMain.rawValue) {\n            windowManager.windowTransitionCoordinator.swapFocusedWindowToMain()\n        }\n\n        constructCommandWithCommandKey(CommandKey.displayCurrentLayout.rawValue) {\n            DispatchQueue.main.async {\n                windowManager.displayCurrentLayout()\n            }\n        }\n\n        (1...5).forEach { screenNumber in\n            let focusCommandKey = \"\\(CommandKey.focusScreenPrefix.rawValue)-\\(screenNumber)\"\n            let throwCommandKey = \"\\(CommandKey.throwScreenPrefix.rawValue)-\\(screenNumber)\"\n\n            self.constructCommandWithCommandKey(focusCommandKey) {\n                windowManager.focusTransitionCoordinator.focusScreen(at: screenNumber - 1)\n            }\n\n            self.constructCommandWithCommandKey(throwCommandKey) {\n                windowManager.windowTransitionCoordinator.throwToScreenAtIndex(screenNumber - 1)\n            }\n        }\n\n        (1...16).forEach { spaceNumber in\n            let commandKey = \"\\(CommandKey.throwSpacePrefix.rawValue)-\\(spaceNumber)\"\n\n            self.constructCommandWithCommandKey(commandKey) {\n                windowManager.windowTransitionCoordinator.pushFocusedWindowToSpace(spaceNumber - 1)\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.throwSpaceLeft.rawValue) {\n            windowManager.windowTransitionCoordinator.pushFocusedWindowToSpaceLeft()\n        }\n\n        constructCommandWithCommandKey(CommandKey.throwSpaceRight.rawValue) {\n            windowManager.windowTransitionCoordinator.pushFocusedWindowToSpaceRight()\n        }\n\n        constructCommandWithCommandKey(CommandKey.toggleFloat.rawValue) {\n            windowManager.toggleFloatForFocusedWindow()\n        }\n\n        constructCommandWithCommandKey(CommandKey.toggleTiling.rawValue) {\n            self.userConfiguration.tilingEnabled = !self.userConfiguration.tilingEnabled\n            windowManager.markAllScreensForReflow()\n        }\n\n        constructCommandWithCommandKey(CommandKey.enableTiling.rawValue) {\n            guard !self.userConfiguration.tilingEnabled else { return }\n            self.userConfiguration.tilingEnabled = true\n            windowManager.markAllScreensForReflow()\n        }\n\n        constructCommandWithCommandKey(CommandKey.disableTiling.rawValue) {\n            guard self.userConfiguration.tilingEnabled else { return }\n            self.userConfiguration.tilingEnabled = false\n            windowManager.markAllScreensForReflow()\n        }\n\n        constructCommandWithCommandKey(CommandKey.reevaluateWindows.rawValue) {\n            windowManager.reevaluateWindows()\n        }\n\n        constructCommandWithCommandKey(CommandKey.toggleFocusFollowsMouse.rawValue) {\n            self.userConfiguration.toggleFocusFollowsMouse()\n        }\n\n        constructCommandWithCommandKey(CommandKey.relaunchAmethyst.rawValue) { [weak appDelegate] in\n            appDelegate?.relaunch(self)\n        }\n\n        constructCommandWithCommandKey(CommandKey.increaseWindowMaxCount.rawValue) {\n            self.userConfiguration.increaseWindowMaxCount()\n            windowManager.markAllScreensForReflow()\n            DispatchQueue.main.async {\n                windowManager.displayWindowCountHUD()\n            }\n        }\n\n        constructCommandWithCommandKey(CommandKey.decreaseWindowMaxCount.rawValue) {\n            self.userConfiguration.decreaseWindowMaxCount()\n            windowManager.markAllScreensForReflow()\n            DispatchQueue.main.async {\n                windowManager.displayWindowCountHUD()\n            }\n        }\n\n        LayoutType<Application.Window>.availableLayoutStrings().forEach { (layoutKey, _) in\n            self.constructCommandWithCommandKey(UserConfiguration.constructLayoutKeyString(layoutKey)) {\n                let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()\n                screenManager?.selectLayout(layoutKey)\n            }\n        }\n    }\n\n    private func constructKeyCodeMap() -> [String: [AMKeyCode]] {\n        var stringToKeyCodes: [String: [AMKeyCode]] = [:]\n\n        // Generate unicode character keymapping from keyboard layout data.  We go\n        // through all keycodes and create a map of string representations to a list\n        // of key codes. It has to map to a list because a string representation\n        // canmap to multiple codes (e.g., 1 and numpad 1 both have string\n        // representation \"1\").\n        var currentKeyboard = TISCopyCurrentKeyboardInputSource().takeRetainedValue()\n        var rawLayoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData)\n\n        if rawLayoutData == nil {\n            currentKeyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource().takeUnretainedValue()\n            rawLayoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData)\n        }\n\n        // Get the layout\n        let layoutData = unsafeBitCast(rawLayoutData, to: CFData.self)\n        let layout: UnsafePointer<UCKeyboardLayout> = unsafeBitCast(CFDataGetBytePtr(layoutData), to: UnsafePointer<UCKeyboardLayout>.self)\n\n        var keysDown: UInt32 = 0\n        var chars: [UniChar] = [0, 0, 0, 0]\n        var realLength: Int = 0\n\n        for keyCode in (0..<AMKeyCodeInvalid) {\n            switch keyCode {\n            case\n            kVK_ANSI_Keypad0,\n            kVK_ANSI_Keypad1,\n            kVK_ANSI_Keypad2,\n            kVK_ANSI_Keypad3,\n            kVK_ANSI_Keypad4,\n            kVK_ANSI_Keypad5,\n            kVK_ANSI_Keypad6,\n            kVK_ANSI_Keypad7,\n            kVK_ANSI_Keypad8,\n            kVK_ANSI_Keypad9:\n                continue\n            default:\n                break\n            }\n\n            UCKeyTranslate(layout,\n                           UInt16(keyCode),\n                           UInt16(kUCKeyActionDisplay),\n                           0,\n                           UInt32(LMGetKbdType()),\n                           UInt32(kUCKeyTranslateNoDeadKeysBit),\n                           &keysDown,\n                           chars.count,\n                           &realLength,\n                           &chars)\n\n            let string = CFStringCreateWithCharacters(kCFAllocatorDefault, chars, realLength) as String\n\n            if let keyCodes = stringToKeyCodes[string] {\n                var mutableKeyCodes = keyCodes\n                mutableKeyCodes.append(keyCode)\n                stringToKeyCodes[string] = mutableKeyCodes\n            } else {\n                stringToKeyCodes[string] = [keyCode]\n            }\n        }\n\n        // Add codes for non-printable characters. They are not printable so they\n        // are not generated from the keyboard layout data.\n        stringToKeyCodes[\"space\"] = [kVK_Space]\n        stringToKeyCodes[\"enter\"] = [kVK_Return]\n        stringToKeyCodes[\"up\"] = [kVK_UpArrow]\n        stringToKeyCodes[\"right\"] = [kVK_RightArrow]\n        stringToKeyCodes[\"down\"] = [kVK_DownArrow]\n        stringToKeyCodes[\"left\"] = [kVK_LeftArrow]\n        stringToKeyCodes[\"delete\"] = [kVK_Delete]\n\n        return stringToKeyCodes\n    }\n\n    private func constructCommandWithCommandKey(_ commandKey: String, handler: @escaping HotKeyHandler) {\n        userConfiguration.constructCommand(for: self, commandKey: commandKey, handler: handler)\n    }\n\n    private func carbonModifiersFromModifiers(_ modifiers: UInt) -> UInt32 {\n        var carbonModifiers: UInt32 = 0\n\n        if (modifiers & UInt(NSEvent.ModifierFlags.shift.rawValue)) > 0 {\n            carbonModifiers = carbonModifiers | UInt32(shiftKey)\n        }\n\n        if (modifiers & UInt(NSEvent.ModifierFlags.command.rawValue)) > 0 {\n            carbonModifiers = carbonModifiers | UInt32(cmdKey)\n        }\n\n        if (modifiers & UInt(NSEvent.ModifierFlags.option.rawValue)) > 0 {\n            carbonModifiers = carbonModifiers | UInt32(optionKey)\n        }\n\n        if (modifiers & UInt(NSEvent.ModifierFlags.control.rawValue)) > 0 {\n            carbonModifiers = carbonModifiers | UInt32(controlKey)\n        }\n\n        return carbonModifiers\n    }\n\n    static func hotKeyNameToDefaultsKey() -> [[String]] {\n        var hotKeyNameToDefaultsKey: [[String]] = []\n\n        hotKeyNameToDefaultsKey.append([\"Cycle layout forward\", CommandKey.cycleLayoutForward.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Cycle layout backwards\", CommandKey.cycleLayoutBackward.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Shrink main pane\", CommandKey.shrinkMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Expand main pane\", CommandKey.expandMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Increase main pane count\", CommandKey.increaseMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Decrease main pane count\", CommandKey.decreaseMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Increase window max count\", CommandKey.increaseWindowMaxCount.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Decrease window max count\", CommandKey.decreaseWindowMaxCount.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Custom layout command 1\", CommandKey.command1.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Custom layout command 2\", CommandKey.command2.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Custom layout command 3\", CommandKey.command3.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Custom layout command 4\", CommandKey.command4.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Move focus counter clockwise\", CommandKey.focusCCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Move focus clockwise\", CommandKey.focusCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Move focus to main window\", CommandKey.focusMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Move focus to counter clockwise screen\", CommandKey.focusScreenCCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Move focus to clockwise screen\", CommandKey.focusScreenCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Swap focused window to counter clockwise screen\", CommandKey.swapScreenCCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Swap focused window to clockwise screen\", CommandKey.swapScreenCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Swap focused window counter clockwise\", CommandKey.swapCCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Swap focused window clockwise\", CommandKey.swapCW.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Swap focused window with main window\", CommandKey.swapMain.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Force windows to be reevaluated\", CommandKey.reevaluateWindows.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Throw focused window to space left\", CommandKey.throwSpaceLeft.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Throw focused window to space right\", CommandKey.throwSpaceRight.rawValue])\n\n        (1...16).forEach { spaceNumber in\n            let name = \"Throw focused window to space \\(spaceNumber)\"\n\n            hotKeyNameToDefaultsKey.append([name, \"\\(CommandKey.throwSpacePrefix.rawValue)-\\(spaceNumber)\"])\n        }\n\n        (1...5).forEach { screenNumber in\n            let focusCommandName = \"Focus screen \\(screenNumber)\"\n            let throwCommandName = \"Throw focused window to screen \\(screenNumber)\"\n            let focusCommandKey = \"\\(CommandKey.focusScreenPrefix.rawValue)-\\(screenNumber)\"\n            let throwCommandKey = \"\\(CommandKey.throwScreenPrefix.rawValue)-\\(screenNumber)\"\n\n            hotKeyNameToDefaultsKey.append([focusCommandName, focusCommandKey])\n            hotKeyNameToDefaultsKey.append([throwCommandName, throwCommandKey])\n        }\n\n        hotKeyNameToDefaultsKey.append([\"Toggle float for focused window\", CommandKey.toggleFloat.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Display current layout\", CommandKey.displayCurrentLayout.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Toggle focus follows mouse\", CommandKey.toggleFocusFollowsMouse.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Toggle global tiling\", CommandKey.toggleTiling.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Enable global tiling\", CommandKey.enableTiling.rawValue])\n        hotKeyNameToDefaultsKey.append([\"Disable global tiling\", CommandKey.disableTiling.rawValue])\n\n        for (layoutKey, layoutName) in LayoutType<Application.Window>.availableLayoutStrings() {\n            let commandName = \"Select \\(layoutName) layout\"\n            let commandKey = \"select-\\(layoutKey)-layout\"\n            hotKeyNameToDefaultsKey.append([commandName, commandKey])\n        }\n\n        hotKeyNameToDefaultsKey.append([\"Relaunch Amethyst\", CommandKey.relaunchAmethyst.rawValue])\n\n        return hotKeyNameToDefaultsKey\n    }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/123.rectangle.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"123.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/amethyst.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/dots.and.line.vertical.and.cursorarrow.rectangle.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"dots.and.line.vertical.and.cursorarrow.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/icon-statusitem-disabled.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-statusitem-disabled.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"icon-statusitem-disabled@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/icon-statusitem.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-statusitem.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"icon-statusitem@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/macwindow.on.rectangle.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macwindow.on.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/text.and.command.macwindow.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"text.and.command.macwindow.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/uiwindow.split.2x1.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"uiwindow.split.2x1.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/waveform.path.ecg.rectangle.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"waveform.path.ecg.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layout.swift",
    "content": "//\n//  Layout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/3/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\n/**\n A base class for specific layout algorithms defining size and position of windows.\n \n - Requires:\n Specific layouts must subclass and override the following properties and methods:\n - `layoutName`\n - `layoutKey`\n \n Subclasses can optionally override `layoutDescription` to provide debugging information for the layout state.\n \n - Note:\n Usage of a layout object requires specifying a `WindowType` parameter.\n */\nclass Layout<Window: WindowType>: Codable {\n    typealias Screen = Window.Screen\n\n    private enum CodingKeys: String, CodingKey {\n        case key\n    }\n\n    /// The display name of the layout.\n    class var layoutName: String { fatalError(\"Must be implemented by subclass\") }\n\n    /// The configuration key of the layout.\n    class var layoutKey: String { fatalError(\"Must be implemented by subclass\") }\n\n    /// The display name of the layout.\n    var layoutName: String { return type(of: self).layoutName }\n\n    /// The configuration key of the layout.\n    var layoutKey: String { return type(of: self).layoutKey }\n\n    /// The debug description of the layout.\n    var layoutDescription: String { return \"\" }\n\n    required init() {}\n\n    required init(from decoder: Decoder) throws {}\n\n    /// Base encoder for layouts; basically a noop.\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(layoutKey, forKey: .key)\n    }\n\n    /**\n     Takes a list of windows and a screen and returns the assignments that would be performed.\n     \n     - Parameters:\n         - windows: The windows to apply the layout algorithm to.\n         - screen: The screen on which those windows should reside.\n     \n     - Returns:\n     The assignments that would be performed given those windows on that screen.\n     */\n    func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        fatalError(\"Must be implemented by subclass\")\n    }\n}\n\n/// Errors occurring when decoding a layout\nenum LayoutDecodingError: Error {\n    /**\n     Something about the layout was structurally unsound.\n     \n     Notable example: bsp layout cannot recover if some windows are no longer present, so if we fail to decode a node the layout is no longer sound.\n     */\n    case invalidLayout\n}\n\n// MARK: Window Querying\n\nextension Layout {\n    /**\n     Determines what window the layout would put at a given point.\n     \n     - Parameters:\n         - point: The point to test for location.\n         - windows: The windows to apply the layout algorithm to.\n         - screen: The screen on which those windows should reside.\n     \n     - Returns:\n     The window that the layout would intend to put at `point`.\n     \n     - Note: This does not necessarily correspond to the final position of the window as windows do not necessarily take the exact frame the layout provides.\n     */\n    func windowAtPoint(_ point: CGPoint, of windowSet: WindowSet<Window>, on screen: Screen) -> LayoutWindow<Window>? {\n        return frameAssignments(windowSet, on: screen)?\n            .map { $0.frameAssignment }\n            .first { $0.frame.contains(point) }?\n            .window\n    }\n\n    /**\n     Determines what frame the layout would apply to a given window.\n     \n     - Parameters:\n         - window: The window to test for frame.\n         - windows: The windows to apply the layout algorithm to.\n         - screen: The screen on which those windows should reside.\n     \n     - Returns:\n     The `FrameAssignment` object defining the size and location that the layout would assign to `window`.\n     \n     - Note: This does not necessarily correspond to the final frame of the window as windows do not necessarily take the exact frame the layout provides.\n     */\n    func assignedFrame(_ window: Window, of windowSet: WindowSet<Window>, on screen: Screen) -> FrameAssignment<Window>? {\n        guard let assignments = frameAssignments(windowSet, on: screen) else {\n            return nil\n        }\n\n        return assignments.map { $0.frameAssignment }.first { $0.window.id == window.id() }\n    }\n}\n\n/**\n A particular kind of layout that organizes windows into a main pane and any number of sub-panes.\n \n - Note:\n The definition is intentionally somewhat layout. This is more intended to demonstrate the expected interface for a fairly common paradigm in Amethyst layouts.\n */\nprotocol PanedLayout {\n    /**\n     The ratio of the size of the main pane to the size of the sub-panes.\n     \n     - Requires:\n     The value must be between 0 and 1, inclusive.\n     */\n    var mainPaneRatio: CGFloat { get }\n\n    /// The number of windows that make up the main pane.\n    var mainPaneCount: Int { get }\n\n    /**\n     Takes a direct recommendation for a change in ratio.\n     \n     - Parameters:\n        - rawRatio: The ratio recommended by the caller.\n     \n     - Requires:\n     `rawRatio` must be a valid ratio.\n     \n     - Note: This method should generally be reserved for internal use by the layout.\n     */\n    func recommendMainPaneRawRatio(rawRatio: CGFloat)\n\n    /// Reduces the visual footprint of the main pane relative to the sub-panes.\n    func shrinkMainPane()\n\n    /// Increases the visual footprint of the main pane relative to the sub-panes.\n    func expandMainPane()\n\n    /// Increases the number of windows that make up the main pane.\n    func increaseMainPaneCount()\n\n    /// Decreases the number of windows that make up the main pane.\n    func decreaseMainPaneCount()\n}\n\nextension PanedLayout {\n    /// The default debug layout description for paned layouts. It describes the ratio and number of main pane windows.\n    var layoutDescription: String {\n        return \"(\\(mainPaneRatio), \\(mainPaneCount))\"\n    }\n\n    /**\n     Takes a recommendation for a change in ratio, but can modify the ratio to adjust for internal state.\n     \n     - Parameters:\n        - ratio: The ratio recommended by the caller.\n     */\n    func recommendMainPaneRatio(_ ratio: CGFloat) {\n        guard 0 <= ratio && ratio <= 1 else {\n            log.warning(\"tried to setMainPaneRatio out of range [0-1]:  \\(ratio)\")\n            return recommendMainPaneRawRatio(rawRatio: max(min(ratio, 1), 0))\n        }\n        recommendMainPaneRawRatio(rawRatio: ratio)\n    }\n\n    /// The default behavior of main pane expansion that simply recommends an increase in ratio by the configured resize step.\n    func expandMainPane() {\n        recommendMainPaneRatio(mainPaneRatio + UserConfiguration.shared.windowResizeStep())\n    }\n\n    /// The default behavior of main pane shrinking that simply recommends a decrease in ratio by the configured resize step.\n    func shrinkMainPane() {\n        recommendMainPaneRatio(mainPaneRatio - UserConfiguration.shared.windowResizeStep())\n    }\n}\n\n/**\n A base class for specific layout algorithms that maintain internal state for defining size and position of windows.\n \n - Requires:\n Specific layouts must subclass and override the following properties and methods:\n - `updateWithChange(_ windowChange: WindowChange<Window>)`\n - `nextWindowIDCounterClockwise() -> CGWindowID?`\n - `nextWindowIDClockwise() -> CGWindowID?`\n \n Notably, the latter two are necessary for the window manager to determine flow of windows. By default layouts are a simple linear list, but more complex layouts may have different logic.\n */\nclass StatefulLayout<Window: WindowType>: Layout<Window> {\n    /**\n     Updates internal state of the layout based on a window change.\n     \n     - Parameters:\n        - windowChange: A `WindowChange`.\n     */\n    func updateWithChange(_ windowChange: Change<Window>) {\n        fatalError(\"Must be implemented by subclass\")\n    }\n\n    /**\n     Determines the window that is before the current window.\n     \n     - Returns:\n     The ID of the window before the current window.\n     */\n    func nextWindowIDCounterClockwise() -> Window.WindowID? {\n        fatalError(\"Must be implemented by subclass\")\n    }\n\n    /**\n     Determines the window that is after the current window.\n     \n     - Returns:\n     The ID of the window after the current window.\n     */\n    func nextWindowIDClockwise() -> Window.WindowID? {\n        fatalError(\"Must be implemented by subclass\")\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/BinarySpacePartitioningLayout.swift",
    "content": "//\n//  BinarySpacePartitioningLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/29/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass TreeNode<Window: WindowType>: Codable {\n    typealias WindowID = Window.WindowID\n\n    private enum CodingKeys: String, CodingKey {\n        case left\n        case right\n        case windowID\n    }\n\n    weak var parent: TreeNode?\n    var left: TreeNode?\n    var right: TreeNode?\n    var windowID: WindowID?\n\n    init() {}\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.left = try values.decodeIfPresent(TreeNode.self, forKey: .left)\n        self.right = try values.decodeIfPresent(TreeNode.self, forKey: .right)\n        self.windowID = try values.decodeIfPresent(WindowID.self, forKey: .windowID)\n\n        self.left?.parent = self\n        self.right?.parent = self\n\n        guard valid else {\n            throw LayoutDecodingError.invalidLayout\n        }\n    }\n\n    var valid: Bool {\n        return (left != nil && right != nil && windowID == nil) || (left == nil && right == nil && windowID != nil)\n    }\n\n    func findWindowID(_ windowID: WindowID) -> TreeNode? {\n        guard self.windowID == windowID else {\n            return left?.findWindowID(windowID) ?? right?.findWindowID(windowID)\n        }\n\n        return self\n    }\n\n    func orderedWindowIDs() -> [WindowID] {\n        guard let windowID = windowID else {\n            let leftWindowIDs = left?.orderedWindowIDs() ?? []\n            let rightWindowIDs = right?.orderedWindowIDs() ?? []\n            return leftWindowIDs + rightWindowIDs\n        }\n\n        return [windowID]\n    }\n\n    func insertWindowIDAtEnd(_ windowID: WindowID) {\n        guard left == nil && right == nil else {\n            right?.insertWindowIDAtEnd(windowID)\n            return\n        }\n\n        insertWindowID(windowID)\n    }\n\n    func insertWindowID(_ windowID: WindowID, atPoint insertionPoint: WindowID) {\n        guard self.windowID == insertionPoint else {\n            left?.insertWindowID(windowID, atPoint: insertionPoint)\n            right?.insertWindowID(windowID, atPoint: insertionPoint)\n            return\n        }\n\n        insertWindowID(windowID)\n    }\n\n    func removeWindowID(_ windowID: WindowID) {\n        guard let node = findWindowID(windowID) else {\n            log.error(\"Trying to remove window not in tree\")\n            return\n        }\n\n        guard let parent = node.parent else {\n            return\n        }\n\n        guard let grandparent = parent.parent else {\n            if node == parent.left {\n                parent.windowID = parent.right?.windowID\n            } else {\n                parent.windowID = parent.left?.windowID\n            }\n            parent.left = nil\n            parent.right = nil\n            return\n        }\n\n        if parent == grandparent.left {\n            if node == parent.left {\n                grandparent.left = parent.right\n            } else {\n                grandparent.left = parent.left\n            }\n            grandparent.left?.parent = grandparent\n        } else {\n            if node == parent.left {\n                grandparent.right = parent.right\n            } else {\n                grandparent.right = parent.left\n            }\n            grandparent.right?.parent = grandparent\n        }\n    }\n\n    func insertWindowID(_ windowID: WindowID) {\n        guard parent != nil || self.windowID != nil else {\n            self.windowID = windowID\n            return\n        }\n\n        if let parent = parent {\n            let newParent = TreeNode()\n            let newNode = TreeNode()\n\n            newNode.parent = newParent\n            newNode.windowID = windowID\n\n            newParent.left = self\n            newParent.right = newNode\n            newParent.parent = parent\n\n            if self == parent.left {\n                parent.left = newParent\n            } else {\n                parent.right = newParent\n            }\n\n            self.parent = newParent\n        } else {\n            let newSelf = TreeNode()\n            let newNode = TreeNode()\n\n            newSelf.windowID = self.windowID\n            self.windowID = nil\n\n            newNode.windowID = windowID\n\n            left = newSelf\n            left?.parent = self\n            right = newNode\n            right?.parent = self\n        }\n    }\n}\n\nextension TreeNode: Equatable {\n    static func == (lhs: TreeNode, rhs: TreeNode) -> Bool {\n        return lhs.windowID == rhs.windowID && lhs.left == rhs.left && lhs.right == rhs.right\n    }\n}\n\nclass BinarySpacePartitioningLayout<Window: WindowType>: StatefulLayout<Window> {\n    typealias WindowID = Window.WindowID\n\n    private typealias TraversalNode = (node: TreeNode<Window>, frame: CGRect)\n\n    private enum CodingKeys: String, CodingKey {\n        case rootNode\n    }\n\n    override static var layoutName: String { return \"Binary Space Partitioning\" }\n    override static var layoutKey: String { return \"bsp\" }\n\n    override var layoutDescription: String { return \"\\(lastKnownFocusedWindowID.debugDescription)\" }\n\n    private var rootNode = TreeNode<Window>()\n    private var lastKnownFocusedWindowID: WindowID?\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.rootNode = try container.decode(TreeNode<Window>.self, forKey: .rootNode)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(rootNode, forKey: .rootNode)\n    }\n\n    private func constructInitialTreeWithWindows(_ windows: [LayoutWindow<Window>]) {\n        for window in windows {\n            guard rootNode.findWindowID(window.id) == nil else {\n                continue\n            }\n\n            rootNode.insertWindowIDAtEnd(window.id)\n\n            if window.isFocused {\n                lastKnownFocusedWindowID = window.id\n            }\n        }\n    }\n\n    override func updateWithChange(_ windowChange: Change<Window>) {\n        switch windowChange {\n        case let .add(window):\n            guard rootNode.findWindowID(window.id()) == nil else {\n                log.warning(\"Trying to add a window already in the tree\")\n                return\n            }\n\n            if let insertionPoint = lastKnownFocusedWindowID, window.id() != insertionPoint, rootNode.findWindowID(insertionPoint) != nil {\n                log.info(\"insert \\(window) - \\(window.id()) at point: \\(insertionPoint)\")\n                rootNode.insertWindowID(window.id(), atPoint: insertionPoint)\n            } else {\n                log.info(\"insert \\(window) - \\(window.id()) at end\")\n                rootNode.insertWindowIDAtEnd(window.id())\n            }\n\n            if window.isFocused() {\n                lastKnownFocusedWindowID = window.id()\n            }\n        case let .remove(window):\n            log.info(\"remove: \\(window) - \\(window.id())\")\n            rootNode.removeWindowID(window.id())\n        case let .focusChanged(window):\n            lastKnownFocusedWindowID = window.id()\n        case let .windowSwap(window, otherWindow):\n            let windowID = window.id()\n            let otherWindowID = otherWindow.id()\n\n            guard let windowNode = rootNode.findWindowID(windowID), let otherWindowNode = rootNode.findWindowID(otherWindowID) else {\n                log.error(\"Tried to perform an unbalanced window swap: \\(windowID) <-> \\(otherWindowID)\")\n                return\n            }\n\n            windowNode.windowID = otherWindowID\n            otherWindowNode.windowID = windowID\n        case let .tabChange(window, previousWindow):\n            if rootNode.findWindowID(window.id()) != nil {\n                log.warning(\"Trying to swap a tab in that is already in the tree: \\(window)\")\n                rootNode.removeWindowID(window.id())\n            }\n\n            guard let previousWindowNode = rootNode.findWindowID(previousWindow.id()) else {\n                log.error(\"Trying to change tab from a window that is not in the tree: \\(previousWindow)\")\n                return\n            }\n\n            if let windowNode = rootNode.findWindowID(window.id()) {\n                log.warning(\"Trying to swap a tab in from another node\")\n            }\n\n            previousWindowNode.windowID = window.id()\n        case .applicationDeactivate, .applicationActivate, .spaceChange, .layoutChange, .none, .unknown:\n            break\n        }\n    }\n\n    override func nextWindowIDCounterClockwise() -> WindowID? {\n        guard let focusedWindow = Window.currentlyFocused() else {\n            return nil\n        }\n\n        let orderedIDs = rootNode.orderedWindowIDs()\n\n        guard let focusedWindowIndex = orderedIDs.firstIndex(of: focusedWindow.id()) else {\n            return nil\n        }\n\n        let nextWindowIndex = (focusedWindowIndex == 0 ? orderedIDs.count - 1 : focusedWindowIndex - 1)\n\n        return orderedIDs[nextWindowIndex]\n    }\n\n    override func nextWindowIDClockwise() -> WindowID? {\n        guard let focusedWindow = Window.currentlyFocused() else {\n            return nil\n        }\n\n        let orderedIDs = rootNode.orderedWindowIDs()\n\n        guard let focusedWindowIndex = orderedIDs.firstIndex(of: focusedWindow.id()) else {\n            return nil\n        }\n\n        let nextWindowIndex = (focusedWindowIndex == orderedIDs.count - 1 ? 0 : focusedWindowIndex + 1)\n\n        return orderedIDs[nextWindowIndex]\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        if rootNode.left == nil && rootNode.right == nil {\n            constructInitialTreeWithWindows(windows)\n        }\n\n        let windowIDMap: [WindowID: LayoutWindow<Window>] = windows.reduce([:]) { (windowMap, window) -> [WindowID: LayoutWindow<Window>] in\n            var mutableWindowMap = windowMap\n            mutableWindowMap[window.id] = window\n            return mutableWindowMap\n        }\n\n        let baseFrame = screen.adjustedFrame()\n        var ret: [FrameAssignmentOperation<Window>] = []\n        var traversalNodes: [TraversalNode] = [(node: rootNode, frame: baseFrame)]\n\n        while !traversalNodes.isEmpty {\n            let traversalNode = traversalNodes[0]\n\n            traversalNodes = [TraversalNode](traversalNodes.dropFirst(1))\n\n            if let windowID = traversalNode.node.windowID {\n                guard let window = windowIDMap[windowID] else {\n                    log.warning(\"Could not find window for ID: \\(windowID)\")\n                    continue\n                }\n\n                let resizeRules = ResizeRules(isMain: true, unconstrainedDimension: .horizontal, scaleFactor: 1)\n                let frameAssignment = FrameAssignment<Window>(\n                    frame: traversalNode.frame,\n                    window: window,\n                    screenFrame: baseFrame,\n                    resizeRules: resizeRules\n                )\n                ret.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n            } else {\n                guard let left = traversalNode.node.left, let right = traversalNode.node.right else {\n                    log.error(\"Encountered an invalid node\")\n                    continue\n                }\n\n                let frame = traversalNode.frame\n\n                if frame.width > frame.height {\n                    let leftFrame = CGRect(\n                        x: frame.origin.x,\n                        y: frame.origin.y,\n                        width: frame.width / 2.0,\n                        height: frame.height\n                    )\n                    let rightFrame = CGRect(\n                        x: frame.origin.x + frame.width / 2.0,\n                        y: frame.origin.y,\n                        width: frame.width / 2.0,\n                        height: frame.height\n                    )\n                    traversalNodes.append((node: left, frame: leftFrame))\n                    traversalNodes.append((node: right, frame: rightFrame))\n                } else {\n                    let topFrame = CGRect(\n                        x: frame.origin.x,\n                        y: frame.origin.y,\n                        width: frame.width,\n                        height: frame.height / 2.0\n                    )\n                    let bottomFrame = CGRect(\n                        x: frame.origin.x,\n                        y: frame.origin.y + frame.height / 2.0,\n                        width: frame.width,\n                        height: frame.height / 2.0\n                    )\n                    traversalNodes.append((node: left, frame: topFrame))\n                    traversalNodes.append((node: right, frame: bottomFrame))\n                }\n            }\n        }\n\n        return ret\n    }\n}\n\nextension BinarySpacePartitioningLayout: Equatable {\n    static func == (lhs: BinarySpacePartitioningLayout<Window>, rhs: BinarySpacePartitioningLayout<Window>) -> Bool {\n        return lhs.rootNode == rhs.rootNode\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/ColumnLayout.swift",
    "content": "//\n//  ColumnLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass ColumnLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Column\" }\n    override static var layoutKey: String { return \"column\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count - mainPaneCount\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n        let mainPaneWidth = round(screenFrame.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))\n        let mainPaneWindowWidth = round(mainPaneWidth / CGFloat(mainPaneCount))\n        let secondaryPaneWindowWidth = hasSecondaryPane ? round((screenFrame.width - mainPaneWidth) / CGFloat(secondaryPaneCount)) : 0.0\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame: CGRect = .zero\n            let isMain = frameAssignments.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.width / mainPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x + (mainPaneWindowWidth * CGFloat(frameAssignments.count))\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = screenFrame.height\n            } else {\n                scaleFactor = (screenFrame.width / secondaryPaneWindowWidth) / CGFloat(secondaryPaneCount)\n                windowFrame.origin.x = screenFrame.origin.x + mainPaneWidth + (secondaryPaneWindowWidth * CGFloat(frameAssignments.count - mainPaneCount))\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = screenFrame.height\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n            let operation = FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)\n\n            assignments.append(operation)\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/CustomLayout.swift",
    "content": "//\n//  CustomLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 7/2/21.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport CommonCrypto\nimport Foundation\nimport JavaScriptCore\n\nprivate struct JSWindow<Window: WindowType> {\n    let id: String\n    let window: LayoutWindow<Window>\n}\n\nprivate extension JSValue {\n    func toRoundedRect() -> CGRect {\n        let rect = toRect()\n        return CGRect(x: round(rect.origin.x), y: round(rect.origin.y), width: round(rect.width), height: round(rect.height))\n    }\n}\n\nprivate enum LayoutExtension<Window: WindowType> {\n    case none\n    case layout(Layout<Window>)\n}\n\nclass CustomLayout<Window: WindowType>: StatefulLayout<Window>, PanedLayout {\n    typealias WindowID = Window.WindowID\n\n    private enum CodingKeys: String, CodingKey {\n        case key\n        case fileURL\n    }\n\n    override static var layoutName: String { return \"Custom\" }\n    override static var layoutKey: String { return \"custom\" }\n\n    override var layoutKey: String {\n        return key\n    }\n\n    override var layoutName: String {\n        return layout?.objectForKeyedSubscript(\"name\").toString() ?? layoutKey\n    }\n\n    var mainPaneRatio: CGFloat { return 1.0 }\n    var mainPaneCount: Int { return 1 }\n\n    private let key: String\n    private let fileURL: URL\n\n    private lazy var context: JSContext? = {\n        guard let context = JSContext() else {\n            log.error(\"Failed to create javascript context\")\n            return nil\n        }\n\n        context.exceptionHandler = { (_: JSContext!, value: JSValue!) in\n            let name = value.objectForKeyedSubscript(\"name\").toString() ?? \"\"\n            let message = value.objectForKeyedSubscript(\"message\").toString() ?? \"\"\n            let stack = value.objectForKeyedSubscript(\"stack\").toString() ?? \"\"\n            log.error(\"\\(name): \\(message)\\n\\(stack)\")\n        }\n\n        context.evaluateScript(\"var console = { log: function(message) { _consoleLog(message) } }\")\n\n        let consoleLog: @convention(block) (String) -> Void = { message in\n            log.debug(message)\n        }\n        context.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: \"_consoleLog\" as (NSCopying & NSObjectProtocol))\n\n        do {\n            context.evaluateScript(try String(contentsOf: self.fileURL))\n        } catch {\n            log.error(error)\n            return nil\n        }\n\n        context.evaluateScript(\"\"\"\n        function sanitizeArguments(fn) {\n            return function(...args) {\n                const sanitizedArgs = args.map(arg => !!arg ? JSON.parse(JSON.stringify(arg)) : undefined);\n                return fn(...sanitizedArgs);\n            };\n        }\n\n        function normalizedLayout() {\n            const l = layout();\n            l.getFrameAssignments = sanitizeArguments(l.getFrameAssignments);\n            return l;\n        }\n        \"\"\")\n\n        return context\n    }()\n\n    private lazy var layout: JSValue? = {\n        return self.context?.objectForKeyedSubscript(\"normalizedLayout\")?.call(withArguments: [])\n    }()\n\n    private lazy var state: JSValue? = {\n        return self.layout?.objectForKeyedSubscript(\"initialState\")\n    }()\n\n    private lazy var commands: JSValue? = {\n        return self.layout?.objectForKeyedSubscript(\"commands\")\n    }()\n\n    private lazy var layoutExtension: LayoutExtension<Window> = {\n        guard let extendedLayoutKey = self.layout?.objectForKeyedSubscript(\"extends\"), extendedLayoutKey.isString else {\n            return .none\n        }\n\n        guard let layout = LayoutType<Window>.layoutForKey(extendedLayoutKey.toString()) else {\n            return .none\n        }\n\n        return .layout(layout)\n    }()\n\n    required init() {\n        fatalError(\"must be constructed with a file\")\n    }\n\n    required init(key: String, fileURL: URL) {\n        self.key = key\n        self.fileURL = fileURL\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.key = try values.decode(String.self, forKey: .key)\n        self.fileURL = try values.decode(URL.self, forKey: .fileURL)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(key, forKey: .key)\n        try container.encode(fileURL, forKey: .fileURL)\n    }\n\n    private func extendedFrameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        switch layoutExtension {\n        case .none:\n            return nil\n        case .layout(let layout):\n            return layout.frameAssignments(windowSet, on: screen)\n        }\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let screenFrame = screen.adjustedFrame()\n        let jsScreenFrameArg = JSValue(rect: screenFrame, in: context)!\n        let jsWindows: [WindowID: JSWindow<Window>] = windows.reduce([:]) { partialResult, layoutWindow in\n            let id = idHash(forWindowID: layoutWindow.id) ?? UUID().uuidString\n            let window = JSWindow<Window>(id: id, window: layoutWindow)\n            return partialResult.merging([layoutWindow.id: window]) { current, _ in return current }\n        }\n        let jsWindowsArg = windows.map { window -> [String: Any?] in\n            let jsWindow = jsWindows[window.id]!\n            return [\n                \"id\": jsWindow.id,\n                \"frame\": JSValue(rect: jsWindow.window.frame, in: context),\n                \"isFocused\": jsWindow.window.isFocused\n            ]\n        }\n\n        let extendedFrames: [[String: Any?]]? = extendedFrameAssignments(windowSet, on: screen)?.compactMap { frameAssignmentOperation in\n            let frameAssignment = frameAssignmentOperation.frameAssignment\n            guard let jsWindow = jsWindows[frameAssignment.window.id] else {\n                return nil\n            }\n            return [\n                \"id\": jsWindow.id,\n                \"frame\": JSValue(rect: frameAssignment.frame, in: context),\n                \"isFocused\": jsWindow.window.isFocused\n            ]\n        }\n        let args: [Any] = [\n            jsWindowsArg,\n            jsScreenFrameArg,\n            state ?? JSValue(undefinedIn: context)!,\n            extendedFrames ?? JSValue(undefinedIn: context)!\n        ]\n\n        guard let getAssignments = layout?.objectForKeyedSubscript(\"getFrameAssignments\"), !getAssignments.isNull && !getAssignments.isUndefined else {\n            return nil\n        }\n\n        guard let assignments = getAssignments.call(withArguments: args), assignments.isObject else {\n            return nil\n        }\n\n        return windows.compactMap { window -> FrameAssignmentOperation<Window>? in\n            guard let jsWindow = jsWindows[window.id] else {\n                return nil\n            }\n\n            guard let frame = assignments.objectForKeyedSubscript(jsWindow.id) else {\n                return nil\n            }\n\n            var unconstrainedDimension: UnconstrainedDimension = .horizontal\n            var scaleFactor = screenFrame.width / frame.toRoundedRect().width\n\n            if let dimension = frame.objectForKeyedSubscript(\"unconstrainedDimension\")?.toString() {\n                switch dimension {\n                case \"horizontal\":\n                    unconstrainedDimension = .horizontal\n                case \"vertical\":\n                    unconstrainedDimension = .vertical\n                    scaleFactor = screenFrame.height / frame.toRoundedRect().height\n                default:\n                    log.warning(\"Encountered unknown unconstrainedDimension value: \\(dimension), defaulting to horizontal\")\n                    unconstrainedDimension = .horizontal\n                }\n            }\n\n            let isMain = frame.objectForKeyedSubscript(\"isMain\")?.toBool() ?? true\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: unconstrainedDimension, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: frame.toRoundedRect(),\n                window: jsWindow.window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n            return FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)\n        }\n    }\n\n    override func updateWithChange(_ windowChange: Change<Window>) {\n        guard let updateWithChange = layout?.objectForKeyedSubscript(\"updateWithChange\"), !updateWithChange.isNull && !updateWithChange.isUndefined else {\n            return\n        }\n\n        let updateWithChangeArgs: [Any]? = state.flatMap { state in\n            return [jsChange(forChange: windowChange), state]\n        }\n\n        guard let updatedState = updateWithChange.call(withArguments: updateWithChangeArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {\n            log.error(\"\\(layoutKey)): received invalid updated state\")\n            return\n        }\n\n        state = updatedState\n    }\n\n    func command1() {\n        command(key: \"command1\")\n    }\n\n    func command2() {\n        command(key: \"command2\")\n    }\n\n    func command3() {\n        command(key: \"command3\")\n    }\n\n    func command4() {\n        command(key: \"command4\")\n    }\n\n    override func nextWindowIDClockwise() -> Window.WindowID? {\n        return nil\n    }\n\n    override func nextWindowIDCounterClockwise() -> Window.WindowID? {\n        return nil\n    }\n\n    private func command(key: String) {\n        guard let command = commands?.objectForKeyedSubscript(key), command.isObject else {\n            log.debug(\"\\(layoutKey) — \\(key): no command defined\")\n            return\n        }\n\n        guard let updateState = command.objectForKeyedSubscript(\"updateState\"), !updateState.isNull && !updateState.isUndefined else {\n            log.debug(\"\\(layoutKey) — \\(key): no updateState function provided\")\n            return\n        }\n\n        let focusedWindowID = Window.currentlyFocused().flatMap { idHash(forWindowID: $0.id()) }\n        let updateStateArgs: [Any]? = state.flatMap { state in\n            if let id = focusedWindowID {\n                return [state, id]\n            } else {\n                return [state]\n            }\n        }\n\n        guard let updatedState = updateState.call(withArguments: updateStateArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {\n            log.error(\"\\(layoutKey) — \\(key): received invalid updated state\")\n            return\n        }\n\n        state = updatedState\n    }\n\n    private func idHash(forWindowID windowID: WindowID) -> String? {\n        do {\n            let encoder = JSONEncoder()\n            encoder.outputFormatting = .sortedKeys\n\n            let encodedID = try encoder.encode(windowID)\n            var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))\n            encodedID.withUnsafeBytes {\n                _ = CC_SHA256($0.baseAddress, CC_LONG(encodedID.count), &hash)\n            }\n            return hash.map { String(format: \"%02hhx\", $0) }.joined()\n        } catch {\n            log.warning(\"Failed to hash window id: \\(error)\")\n            return nil\n        }\n    }\n\n    private func jsChange(forChange change: Change<Window>) -> [String: String] {\n        var jsChange: [String: String] = [:]\n\n        switch change {\n        case .add(window: let window):\n            jsChange[\"change\"] = \"add\"\n            jsChange[\"windowID\"] = idHash(forWindowID: window.id())\n        case .remove(window: let window):\n            jsChange[\"change\"] = \"remove\"\n            jsChange[\"windowID\"] = idHash(forWindowID: window.id())\n        case .focusChanged(window: let window):\n            jsChange[\"change\"] = \"focus_changed\"\n            jsChange[\"windowID\"] = idHash(forWindowID: window.id())\n        case .windowSwap(window: let window, otherWindow: let otherWindow):\n            jsChange[\"change\"] = \"window_swap\"\n            jsChange[\"windowID\"] = idHash(forWindowID: window.id())\n            jsChange[\"otherWindowID\"] = idHash(forWindowID: otherWindow.id())\n        case .applicationActivate:\n            jsChange[\"change\"] = \"application_activate\"\n        case .applicationDeactivate:\n            jsChange[\"change\"] = \"application_deactivate\"\n        case .spaceChange:\n            jsChange[\"change\"] = \"space_change\"\n        case .layoutChange:\n            jsChange[\"change\"] = \"layout_change\"\n        case .tabChange:\n            jsChange[\"change\"] = \"tab_change\"\n        case .unknown:\n            jsChange[\"change\"] = \"unknown\"\n        case .none:\n            jsChange[\"change\"] = \"none\"\n        }\n\n        return jsChange\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        guard\n            let recommendMainPaneRatio = layout?.objectForKeyedSubscript(\"recommendMainPaneRatio\"),\n            !recommendMainPaneRatio.isNull && !recommendMainPaneRatio.isUndefined\n        else {\n            return\n        }\n\n        let recommendMainPaneRatioArgs: [Any]? =  state.flatMap { [rawRatio, $0] }\n\n        guard let updatedState = recommendMainPaneRatio.call(withArguments: recommendMainPaneRatioArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {\n            log.error(\"\\(layoutKey) — recommendMainPaneRawRatio: received invalid updated state\")\n            return\n        }\n\n        state = updatedState\n    }\n\n    func increaseMainPaneCount() {\n        command(key: \"increaseMain\")\n    }\n\n    func decreaseMainPaneCount() {\n        command(key: \"decreaseMain\")\n    }\n\n    func shrinkMainPane() {\n        command(key: \"shrinkMain\")\n    }\n\n    func expandMainPane() {\n        command(key: \"expandMain\")\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/FloatingLayout.swift",
    "content": "//\n//  FloatingLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass FloatingLayout<Window: WindowType>: Layout<Window> {\n    override static var layoutName: String { return \"Floating\" }\n    override static var layoutKey: String { return \"floating\" }\n\n    override var layoutDescription: String { return \"\" }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        return nil\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/FourColumnLayout.swift",
    "content": "//\n//  FourColumnLayout.swift\n//  Amethyst\n//\n//  Originally created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n//  Modifications by Craig Disselkoen on 09/03/18.\n//  Modifications by Reyk Floeter on 10/28/21.\n//\n\nimport Silica\n\n// we'd like to hide these structures and enums behind fileprivate, but\n// https://bugs.swift.org/browse/SR-47\n\nenum FourColumn {\n    case left\n    case middleLeft\n    case middleRight\n    case right\n}\n\nenum FourPane {\n    case main\n    case secondary\n    case tertiary\n    case quaternary\n}\n\nstruct FourPaneWidths {\n    var left: CGFloat = 0\n    var middleLeft: CGFloat = 0\n    var middleRight: CGFloat = 0\n    var right: CGFloat = 0\n}\n\nstruct QuadruplePaneArrangement {\n    /// number of windows in pane\n    private let paneCount: [FourPane: UInt]\n\n    /// height of windows in pane\n    private let paneWindowHeight: [FourPane: CGFloat]\n\n    /// width of windows in pane\n    private let paneWindowWidth: [FourPane: CGFloat]\n\n    // how panes relate to columns\n    private let panePosition: [FourPane: FourColumn]\n\n    /// how columns relate to panes\n    private let columnDesignation: [FourColumn: FourPane]\n\n    /**\n     - Parameters:\n        - mainPane: which Column is the main Pane\n        - numWindows: how many windows total\n        - numMainPane: how many windows in the main Pane\n        - screenSize: total size of the screen\n        - mainPaneRatio: ratio of the screen taken by main pane\n     */\n    init(mainPane: FourColumn, numWindows: UInt, numMainPane: UInt, screenSize: CGSize, mainPaneRatio: CGFloat) {\n        // forward and reverse mapping of columns to their designations\n        self.panePosition = {\n            switch mainPane {\n            case .left: return [.main: .left, .secondary: .middleLeft, .tertiary: .middleRight, .quaternary: .right]\n            case .middleLeft: return [.main: .middleLeft, .secondary: .middleRight, .tertiary: .left, .quaternary: .right]\n            case .middleRight: return [.main: .middleRight, .secondary: .middleLeft, .tertiary: .right, .quaternary: .left]\n            case .right: return [.main: .right, .secondary: .middleRight, .tertiary: .middleLeft, .quaternary: .left]\n            }\n        }()\n        // swap keys and values for reverse lookup\n        self.columnDesignation = Dictionary(uniqueKeysWithValues: panePosition.map({ ($1, $0) }))\n\n        // calculate how many are in each type\n        let mainPaneCount = min(numWindows, numMainPane)\n        let nonMainCount: UInt = numWindows - mainPaneCount\n        // we do tertiary first because a single window produces a zero in integer division by 2\n        let nonMainPaneCount: UInt = max(nonMainCount / 3, 1)\n        let quaternaryPaneCount = nonMainPaneCount\n        let tertiaryPaneCount = nonMainPaneCount\n        let secondaryPaneCount = nonMainPaneCount + max(nonMainCount, 3) % 3\n        self.paneCount = [.main: mainPaneCount, .secondary: secondaryPaneCount, .tertiary: tertiaryPaneCount, .quaternary: quaternaryPaneCount]\n\n        // calculate heights\n        let screenHeight = screenSize.height\n        self.paneWindowHeight = [\n            .main: round(screenHeight / CGFloat(mainPaneCount)),\n            .secondary: secondaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(secondaryPaneCount)),\n            .tertiary: tertiaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(tertiaryPaneCount)),\n            .quaternary: quaternaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(quaternaryPaneCount))\n        ]\n\n        // calculate widths\n        let screenWidth = screenSize.width\n        let mainWindowWidth = round(screenWidth / 4)\n        let nonMainWindowWidth = round(screenWidth / 4)\n        self.paneWindowWidth = [\n            .main: mainWindowWidth,\n            .secondary: nonMainWindowWidth,\n            .tertiary: nonMainWindowWidth,\n            .quaternary: nonMainWindowWidth\n        ]\n   }\n\n    func count(_ pane: FourPane) -> UInt {\n        return paneCount[pane]!\n    }\n\n    func height(_ pane: FourPane) -> CGFloat {\n        return paneWindowHeight[pane]!\n    }\n\n    func width(_ pane: FourPane) -> CGFloat {\n        return paneWindowWidth[pane]!\n    }\n\n    func firstIndex(_ pane: FourPane) -> UInt {\n        switch pane {\n        case .main: return 0\n        case .secondary: return count(.main)\n        case .tertiary: return count(.main) + count(.secondary)\n        case .quaternary: return count(.main) + count(.secondary) + count(.tertiary)\n        }\n    }\n\n    func pane(ofIndex windowIndex: UInt) -> FourPane {\n        if windowIndex >= firstIndex(.quaternary) {\n            return .quaternary\n        }\n        if windowIndex >= firstIndex(.tertiary) {\n            return .tertiary\n        }\n        if windowIndex >= firstIndex(.secondary) {\n            return .secondary\n        }\n        return .main\n    }\n\n    /// Given a window index, which Pane does it belong to, and which index within that Pane\n    func coordinates(at windowIndex: UInt) -> (FourPane, UInt) {\n        let pane = self.pane(ofIndex: windowIndex)\n        return (pane, windowIndex - firstIndex(pane))\n    }\n\n    /// Get the (height, width) dimensions for a window in the given Pane\n    func windowDimensions(inPane pane: FourPane) -> (CGFloat, CGFloat) {\n        return (height(pane), width(pane))\n    }\n\n    /// Get the Column assignment for the given Pane\n    func column(ofPane pane: FourPane) -> FourColumn {\n        return panePosition[pane]!\n    }\n\n    func pane(ofColumn column: FourColumn) -> FourPane {\n        return columnDesignation[column]!\n    }\n\n    /// Get the column widths in the order (left, middle, right)\n    func widthsLeftToRight() -> FourPaneWidths {\n        return FourPaneWidths(\n            left: width(pane(ofColumn: .left)),\n            middleLeft: width(pane(ofColumn: .middleLeft)),\n            middleRight: width(pane(ofColumn: .middleRight)),\n            right: width(pane(ofColumn: .right))\n        )\n    }\n}\n\n// not an actual Layout, just a base class for the four actual Layouts below\nclass FourColumnLayout<Window: WindowType>: Layout<Window> {\n    class var mainColumn: FourColumn { fatalError(\"Must be implemented by subclass\") }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let screenFrame = screen.adjustedFrame()\n        let paneArrangement = QuadruplePaneArrangement(\n            mainPane: type(of: self).mainColumn,\n            numWindows: UInt(windows.count),\n            numMainPane: UInt(mainPaneCount),\n            screenSize: screenFrame.size,\n            mainPaneRatio: mainPaneRatio\n        )\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame = CGRect.zero\n            let windowIndex: UInt = UInt(frameAssignments.count)\n\n            let (pane, paneIndex) = paneArrangement.coordinates(at: windowIndex)\n\n            let (windowHeight, windowWidth): (CGFloat, CGFloat) = paneArrangement.windowDimensions(inPane: pane)\n            let column: FourColumn = paneArrangement.column(ofPane: pane)\n\n            let widths = paneArrangement.widthsLeftToRight()\n\n            let xorigin: CGFloat = screenFrame.origin.x + {\n                switch column {\n                case .left: return 0.0\n                case .middleLeft: return widths.left\n                case .middleRight: return widths.left + widths.middleLeft\n                case .right: return widths.left + widths.middleLeft + widths.middleRight\n                }\n            }()\n\n            let scaleFactor: CGFloat = screenFrame.width / {\n                if pane == .main {\n                    return paneArrangement.width(.main)\n                }\n                return paneArrangement.width(.secondary) + paneArrangement.width(.tertiary) + paneArrangement.width(.quaternary)\n            }()\n\n            windowFrame.origin.x = xorigin\n            windowFrame.origin.y = screenFrame.origin.y + (windowHeight * CGFloat(paneIndex))\n            windowFrame.size.width = windowWidth\n            windowFrame.size.height = windowHeight\n\n            let isMain = windowIndex < paneArrangement.firstIndex(.secondary)\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n\nextension FourColumnLayout {\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n}\n\n// implement the two variants\nclass FourColumnLeftLayout<Window: WindowType>: FourColumnLayout<Window>, PanedLayout {\n    override static var layoutName: String { return \"4Column Left\" }\n    override static var layoutKey: String { return \"4column-left\" }\n    override static var mainColumn: FourColumn { return .middleLeft }\n}\n\nclass FourColumnRightLayout<Window: WindowType>: FourColumnLayout<Window>, PanedLayout {\n    override static var layoutName: String { return \"4Column Right\" }\n    override static var layoutKey: String { return \"4column-right\" }\n    override static var mainColumn: FourColumn { return .middleRight }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/FullscreenLayout.swift",
    "content": "//\n//  FullscreenLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass FullscreenLayout<Window: WindowType>: Layout<Window> {\n    override static var layoutName: String { return \"Fullscreen\" }\n    override static var layoutKey: String { return \"fullscreen\" }\n\n    override var layoutDescription: String { return \"\" }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let screenFrame = screen.adjustedFrame(disableWindowMargins: UserConfiguration.shared.smartWindowMargins())\n        return windowSet.windows.map { window in\n            let resizeRules = ResizeRules(isMain: true, unconstrainedDimension: .horizontal, scaleFactor: 1)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: screenFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules,\n                disableWindowMargins: UserConfiguration.shared.smartWindowMargins()\n            )\n            return FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/RowLayout.swift",
    "content": "//\n//  RowLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass RowLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Row\" }\n    override static var layoutKey: String { return \"row\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count - mainPaneCount\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n\n        let mainPaneHeight = floor(screenFrame.size.height * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))\n        let mainPaneWindowHeight = floor(mainPaneHeight / CGFloat(mainPaneCount))\n        let secondaryPaneWindowHeight = hasSecondaryPane ? floor((screenFrame.size.height - mainPaneHeight) / CGFloat(secondaryPaneCount)) : 0.0\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame: CGRect = .zero\n            let isMain = frameAssignments.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.size.height / mainPaneWindowHeight\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(frameAssignments.count))\n                windowFrame.size.width = screenFrame.width\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.size.height / secondaryPaneWindowHeight / CGFloat(secondaryPaneCount)\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(mainPaneCount)) + (secondaryPaneWindowHeight * CGFloat(frameAssignments.count - mainPaneCount))\n                windowFrame.size.width = screenFrame.width\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .vertical, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/TallLayout.swift",
    "content": "//\n//  TallLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass TallLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Tall\" }\n    override static var layoutKey: String { return \"tall\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    override var layoutDescription: String { return \"\" }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count - mainPaneCount\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n\n        let mainPaneWindowHeight = round(screenFrame.size.height / CGFloat(mainPaneCount))\n        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.size.height / CGFloat(secondaryPaneCount)) : 0.0\n\n        let mainPaneWindowWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))\n        let secondaryPaneWindowWidth = screenFrame.size.width - mainPaneWindowWidth\n\n        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = acc\n            var windowFrame = CGRect.zero\n            let isMain = acc.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.size.width / mainPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(acc.count))\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x + mainPaneWindowWidth\n                windowFrame.origin.y = screenFrame.origin.y + (secondaryPaneWindowHeight * CGFloat(acc.count - mainPaneCount))\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/TallRightLayout.swift",
    "content": "//\n//  TallRightLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass TallRightLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Tall Right\" }\n    override static var layoutKey: String { return \"tall-right\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count - mainPaneCount\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n\n        let mainPaneWindowHeight = round(screenFrame.size.height / CGFloat(mainPaneCount))\n        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.size.height / CGFloat(secondaryPaneCount)) : 0.0\n\n        let secondaryPaneWindowWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(1.0 - mainPaneRatio) : 0))\n        let mainPaneWindowWidth = screenFrame.size.width - secondaryPaneWindowWidth\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame = CGRect.zero\n            let isMain = frameAssignments.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.size.width / mainPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x + secondaryPaneWindowWidth\n                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(frameAssignments.count))\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y + secondaryPaneWindowHeight * CGFloat(windows.count - (frameAssignments.count + 1))\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/ThreeColumnLayout.swift",
    "content": "//\n//  ThreeColumnLayout.swift\n//  Amethyst\n//\n//  Originally created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n//  Modifications by Craig Disselkoen on 09/03/18.\n//\n\nimport Silica\n\n// we'd like to hide these structures and enums behind fileprivate, but\n// https://bugs.swift.org/browse/SR-47\n\nenum Column {\n    case left\n    case middle\n    case right\n}\n\nenum Pane {\n    case main\n    case secondary\n    case tertiary\n}\n\nstruct TriplePaneArrangement {\n    /// number of windows in pane\n    private let paneCount: [Pane: UInt]\n\n    /// height of windows in pane\n    private let paneWindowHeight: [Pane: CGFloat]\n\n    /// width of windows in pane\n    private let paneWindowWidth: [Pane: CGFloat]\n\n    // how panes relate to columns\n    private let panePosition: [Pane: Column]\n\n    /// how columns relate to panes\n    private let columnDesignation: [Column: Pane]\n\n    /**\n     - Parameters:\n        - mainPane: which Column is the main Pane\n        - numWindows: how many windows total\n        - numMainPane: how many windows in the main Pane\n        - screenSize: total size of the screen\n        - mainPaneRatio: ratio of the screen taken by main pane\n     */\n    init(mainPane: Column, numWindows: UInt, numMainPane: UInt, screenSize: CGSize, mainPaneRatio: CGFloat) {\n        // forward and reverse mapping of columns to their designations\n        self.panePosition = {\n            switch mainPane {\n            case .left:   return [.main: .left, .secondary: .middle, .tertiary: .right]\n            case .middle: return [.main: .middle, .secondary: .left, .tertiary: .right]\n            case .right:  return [.main: .right, .secondary: .left, .tertiary: .middle]\n            }\n        }()\n        // swap keys and values for reverse lookup\n        self.columnDesignation = Dictionary(uniqueKeysWithValues: panePosition.map({ ($1, $0) }))\n\n        // calculate how many are in each type\n        let mainPaneCount = min(numWindows, numMainPane)\n        let nonMainCount: UInt = numWindows - mainPaneCount\n        // we do tertiary first because a single window produces a zero in integer division by 2\n        let tertiaryPaneCount = nonMainCount >> 1\n        let secondaryPaneCount = nonMainCount - tertiaryPaneCount\n        self.paneCount = [.main: mainPaneCount, .secondary: secondaryPaneCount, .tertiary: tertiaryPaneCount]\n\n        // calculate heights\n        let screenHeight = screenSize.height\n        self.paneWindowHeight = [\n            .main: round(screenHeight / CGFloat(mainPaneCount)),\n            .secondary: secondaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(secondaryPaneCount)),\n            .tertiary: tertiaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(tertiaryPaneCount))\n        ]\n\n        // calculate widths\n        let screenWidth = screenSize.width\n        let mainWindowWidth = secondaryPaneCount == 0 ? screenWidth : round(screenWidth * mainPaneRatio)\n        let nonMainWindowWidth = round((screenWidth - mainWindowWidth) / 2)\n        self.paneWindowWidth = [\n            .main: mainWindowWidth,\n            .secondary: nonMainWindowWidth,\n            .tertiary: nonMainWindowWidth\n        ]\n   }\n\n    func count(_ pane: Pane) -> UInt {\n        return paneCount[pane]!\n    }\n\n    func height(_ pane: Pane) -> CGFloat {\n        return paneWindowHeight[pane]!\n    }\n\n    func width(_ pane: Pane) -> CGFloat {\n        return paneWindowWidth[pane]!\n    }\n\n    func firstIndex(_ pane: Pane) -> UInt {\n        switch pane {\n        case .main: return 0\n        case .secondary: return count(.main)\n        case .tertiary: return count(.main) + count(.secondary)\n        }\n    }\n\n    func pane(ofIndex windowIndex: UInt) -> Pane {\n        if windowIndex >= firstIndex(.tertiary) {\n            return .tertiary\n        }\n        if windowIndex >= firstIndex(.secondary) {\n            return .secondary\n        }\n        return .main\n    }\n\n    /// Given a window index, which Pane does it belong to, and which index within that Pane\n    func coordinates(at windowIndex: UInt) -> (Pane, UInt) {\n        let pane = self.pane(ofIndex: windowIndex)\n        return (pane, windowIndex - firstIndex(pane))\n    }\n\n    /// Get the (height, width) dimensions for a window in the given Pane\n    func windowDimensions(inPane pane: Pane) -> (CGFloat, CGFloat) {\n        return (height(pane), width(pane))\n    }\n\n    /// Get the Column assignment for the given Pane\n    func column(ofPane pane: Pane) -> Column {\n        return panePosition[pane]!\n    }\n\n    func pane(ofColumn column: Column) -> Pane {\n        return columnDesignation[column]!\n    }\n\n    /// Get the column widths in the order (left, middle, right)\n    func widthsLeftToRight() -> (CGFloat, CGFloat, CGFloat) {\n        return (width(pane(ofColumn: .left)), width(pane(ofColumn: .middle)), width(pane(ofColumn: .right)))\n    }\n}\n\n// not an actual Layout, just a base class for the three actual Layouts below\nclass ThreeColumnLayout<Window: WindowType>: Layout<Window> {\n    class var mainColumn: Column { fatalError(\"Must be implemented by subclass\") }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let screenFrame = screen.adjustedFrame()\n        let paneArrangement = TriplePaneArrangement(\n            mainPane: type(of: self).mainColumn,\n            numWindows: UInt(windows.count),\n            numMainPane: UInt(mainPaneCount),\n            screenSize: screenFrame.size,\n            mainPaneRatio: mainPaneRatio\n        )\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame = CGRect.zero\n            let windowIndex: UInt = UInt(frameAssignments.count)\n\n            let (pane, paneIndex) = paneArrangement.coordinates(at: windowIndex)\n\n            let (windowHeight, windowWidth): (CGFloat, CGFloat) = paneArrangement.windowDimensions(inPane: pane)\n            let column: Column = paneArrangement.column(ofPane: pane)\n\n            let (leftPaneWidth, middlePaneWidth, _): (CGFloat, CGFloat, CGFloat) = paneArrangement.widthsLeftToRight()\n\n            let xorigin: CGFloat = screenFrame.origin.x + {\n                switch column {\n                case .left: return 0.0\n                case .middle: return leftPaneWidth\n                case .right: return leftPaneWidth + middlePaneWidth\n                }\n            }()\n\n            let scaleFactor: CGFloat = screenFrame.width / {\n                if pane == .main {\n                    return paneArrangement.width(.main)\n                }\n                return paneArrangement.width(.secondary) + paneArrangement.width(.tertiary)\n            }()\n\n            windowFrame.origin.x = xorigin\n            windowFrame.origin.y = screenFrame.origin.y + (windowHeight * CGFloat(paneIndex))\n            windowFrame.size.width = windowWidth\n            windowFrame.size.height = windowHeight\n\n            let isMain = windowIndex < paneArrangement.firstIndex(.secondary)\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n\nextension ThreeColumnLayout {\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n}\n\n// implement the three variants\n\nclass ThreeColumnLeftLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {\n    override static var layoutName: String { return \"3Column Left\" }\n    override static var layoutKey: String { return \"3column-left\" }\n    override static var mainColumn: Column { return .left }\n}\n\nclass ThreeColumnMiddleLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {\n    override static var layoutName: String { return \"3Column Middle\" }\n    // for backwards compatibility with users who still have 'middle-wide' in their active layouts\n    override static var layoutKey: String { return \"middle-wide\" }\n    override static var mainColumn: Column { return .middle }\n}\n\nclass ThreeColumnRightLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {\n    override static var layoutName: String { return \"3Column Right\" }\n    override static var layoutKey: String { return \"3column-right\" }\n    override static var mainColumn: Column { return .right }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/TwoPaneLayout.swift",
    "content": "//\n//  TwoPaneLayout.swift\n//  Amethyst\n//\n//  Created by @mwz on 10/06/2021.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass TwoPaneLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Two Pane\" }\n    override static var layoutKey: String { return \"two-pane\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    override var layoutDescription: String { return \"\" }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {}\n\n    func decreaseMainPaneCount() {}\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count > 1 ? 1 : 0\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n        let isHorizontal = (screenFrame.size.width / screenFrame.size.height) >= 1\n\n        let mainPaneWindowHeight = screenFrame.size.height * (!isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)\n        let secondaryPaneWindowHeight = isHorizontal ? mainPaneWindowHeight : screenFrame.size.height - mainPaneWindowHeight\n\n        let mainPaneWindowWidth = screenFrame.size.width * (isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)\n        let secondaryPaneWindowWidth = !isHorizontal ? mainPaneWindowWidth : screenFrame.size.width - mainPaneWindowWidth\n\n        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = acc\n            var windowFrame = CGRect.zero\n            let isMain = acc.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.size.width / mainPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x + (isHorizontal ? mainPaneWindowWidth : 0)\n                windowFrame.origin.y = screenFrame.origin.y + (isHorizontal ? 0 : mainPaneWindowHeight)\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/TwoPaneRightLayout.swift",
    "content": "//\n//  TwoPaneRightLayout.swift\n//  Amethyst\n//\n//  Created by Anja on 16.06.23.\n//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass TwoPaneRightLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Two Pane Right\" }\n    override static var layoutKey: String { return \"two-pane-right\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    override var layoutDescription: String { return \"\" }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {}\n\n    func decreaseMainPaneCount() {}\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count > 1 ? 1 : 0\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n        let isHorizontal = (screenFrame.size.width / screenFrame.size.height) >= 1\n\n        let mainPaneWindowHeight = screenFrame.size.height * (!isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)\n        let secondaryPaneWindowHeight = isHorizontal ? mainPaneWindowHeight : screenFrame.size.height - mainPaneWindowHeight\n\n        let mainPaneWindowWidth = screenFrame.size.width * (isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)\n        let secondaryPaneWindowWidth = !isHorizontal ? mainPaneWindowWidth : screenFrame.size.width - mainPaneWindowWidth\n\n        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = acc\n            var windowFrame = CGRect.zero\n            let isMain = acc.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.size.width / mainPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x + (isHorizontal ? secondaryPaneWindowWidth : 0)\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth\n                windowFrame.origin.x = screenFrame.origin.x\n                windowFrame.origin.y = screenFrame.origin.y + (isHorizontal ? 0 : mainPaneWindowHeight)\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/WideLayout.swift",
    "content": "//\n//  WideLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass WideLayout<Window: WindowType>: Layout<Window>, PanedLayout {\n    override static var layoutName: String { return \"Wide\" }\n    override static var layoutKey: String { return \"wide\" }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        guard !windows.isEmpty else {\n            return []\n        }\n\n        let secondaryPaneCount = windows.count - mainPaneCount\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n\n        let mainPaneWindowHeight = round(screenFrame.height * CGFloat(hasSecondaryPane ? mainPaneRatio : 1))\n        let secondaryPaneWindowHeight = screenFrame.height - mainPaneWindowHeight\n\n        let mainPaneWindowWidth = round(screenFrame.width / CGFloat(mainPaneCount))\n        let secondaryPaneWindowWidth = hasSecondaryPane ? round(screenFrame.width / CGFloat(secondaryPaneCount)) : 0.0\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame = CGRect.zero\n            let isMain = frameAssignments.count < mainPaneCount\n            var scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = screenFrame.height / mainPaneWindowHeight\n                windowFrame.origin.x = screenFrame.origin.x + (mainPaneWindowWidth * CGFloat(frameAssignments.count))\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = screenFrame.height / secondaryPaneWindowHeight\n                windowFrame.origin.x = screenFrame.origin.x + (secondaryPaneWindowWidth * CGFloat(frameAssignments.count - mainPaneCount))\n                windowFrame.origin.y = screenFrame.origin.y + mainPaneWindowHeight\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .vertical, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n            let operation = FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)\n\n            assignments.append(operation)\n\n            return assignments\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Layout/Layouts/WidescreenTallLayout.swift",
    "content": "//\n//  WidescreenTallLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Silica\n\nclass WidescreenTallLayout<Window: WindowType>: Layout<Window> {\n    class var isRight: Bool { fatalError(\"Must be implemented by subclass\") }\n\n    enum CodingKeys: String, CodingKey {\n        case mainPaneCount\n        case mainPaneRatio\n    }\n\n    private(set) var mainPaneCount: Int = 1\n    private(set) var mainPaneRatio: CGFloat = 0.5\n\n    required init() {\n        super.init()\n    }\n\n    required init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)\n        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)\n        super.init()\n    }\n\n    override func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(mainPaneCount, forKey: .mainPaneCount)\n        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)\n    }\n\n    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {\n        let windows = windowSet.windows\n\n        if windows.count == 0 {\n            return []\n        }\n\n        let mainPaneCount = min(windows.count, self.mainPaneCount)\n        let secondaryPaneCount = windows.count - mainPaneCount\n\n        let hasSecondaryPane = secondaryPaneCount > 0\n\n        let screenFrame = screen.adjustedFrame()\n\n        let mainPaneWindowHeight = screenFrame.height\n        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.height / CGFloat(secondaryPaneCount)) : 0.0\n\n        let mainPaneWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))\n        let mainPaneWindowWidth = round(mainPaneWidth / CGFloat(mainPaneCount))\n        let secondaryPaneWindowWidth = screenFrame.width - mainPaneWidth\n\n        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in\n            var assignments = frameAssignments\n            var windowFrame = CGRect.zero\n            let windowIndex = frameAssignments.count\n            let isMain = windowIndex < mainPaneCount\n            let scaleFactor: CGFloat\n\n            if isMain {\n                scaleFactor = CGFloat(screenFrame.size.width / mainPaneWindowWidth) / CGFloat(mainPaneCount)\n                windowFrame.origin.x = screenFrame.origin.x + mainPaneWindowWidth * CGFloat(windowIndex)\n                if type(of: self).isRight {\n                    windowFrame.origin.x += secondaryPaneWindowWidth\n                }\n                windowFrame.origin.y = screenFrame.origin.y\n                windowFrame.size.width = mainPaneWindowWidth\n                windowFrame.size.height = mainPaneWindowHeight\n            } else {\n                scaleFactor = CGFloat(screenFrame.size.width / secondaryPaneWindowWidth)\n                windowFrame.origin.x = screenFrame.origin.x + mainPaneWidth\n                windowFrame.origin.y = screenFrame.origin.y + (secondaryPaneWindowHeight * CGFloat(windowIndex - mainPaneCount))\n                windowFrame.size.width = secondaryPaneWindowWidth\n                windowFrame.size.height = secondaryPaneWindowHeight\n                if type(of: self).isRight {\n                    windowFrame.origin.x = screenFrame.origin.x\n                }\n            }\n\n            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)\n            let frameAssignment = FrameAssignment<Window>(\n                frame: windowFrame,\n                window: window,\n                screenFrame: screenFrame,\n                resizeRules: resizeRules\n            )\n\n            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))\n\n            return assignments\n        }\n    }\n}\n\nextension WidescreenTallLayout: PanedLayout {\n    func recommendMainPaneRawRatio(rawRatio: CGFloat) {\n        mainPaneRatio = rawRatio\n    }\n\n    func increaseMainPaneCount() {\n        mainPaneCount += 1\n    }\n\n    func decreaseMainPaneCount() {\n        mainPaneCount = max(1, mainPaneCount - 1)\n    }\n}\n\nclass WidescreenTallLayoutLeft<Window: WindowType>: WidescreenTallLayout<Window> {\n    override class var isRight: Bool { return false }\n    override static var layoutName: String { return \"Widescreen Tall\" }\n    override static var layoutKey: String { return \"widescreen-tall\" }\n}\n\nclass WidescreenTallLayoutRight<Window: WindowType>: WidescreenTallLayout<Window> {\n    override class var isRight: Bool { return true }\n    override static var layoutName: String { return \"Widescreen Tall Right\" }\n    override static var layoutKey: String { return \"widescreen-tall-right\" }\n}\n"
  },
  {
    "path": "Amethyst/Layout/ReflowOperation.swift",
    "content": "//\n//  ReflowOperation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/19/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\n/// Possible dimensions without constraints.\nenum UnconstrainedDimension: Int {\n    /// The dimension along the x-axis.\n    case horizontal\n\n    /// The dimension along the y-axis.\n    case vertical\n}\n\n/**\n This struct defines what adjustments to a particular window frame are allowed and tracks its size as a proportion of available space (for use in resize calculations).\n\n Some window resizes reflect valid adjustments to the frame layout.\n \n Some window resizes would not be allowed due to hard constraints.\n */\nstruct ResizeRules {\n    /// Whether or not the resize rule is applying to the main frame.\n    let isMain: Bool\n\n    /// The dimension that is allowed to scale.\n    let unconstrainedDimension: UnconstrainedDimension\n\n    /// the scale factor for the unconstrained dimension.\n    let scaleFactor: CGFloat\n\n    /**\n     Determines the new value of the dimension based on the scale factor.\n     \n     Given a new frame, decide which dimension will be honored and return its size.\n     \n     - Parameters:\n        - frame: The frame to transform.\n        - negatePadding: Whether or not to take padding into account.\n     */\n    func scaledDimension(_ frame: CGRect, negatePadding: Bool) -> CGFloat {\n        let dimension: CGFloat = {\n            switch unconstrainedDimension {\n            case .horizontal: return frame.width\n            case .vertical: return frame.height\n            }\n        }()\n\n        let padding = UserConfiguration.shared.windowMargins() ? UserConfiguration.shared.windowMarginSize() : 0\n        return negatePadding ? dimension + padding : dimension\n    }\n}\n\nstruct LayoutWindow<Window: WindowType>: Equatable {\n    let id: Window.WindowID\n    let frame: CGRect\n    let isFocused: Bool\n\n    static func == (lhs: Self, rhs: Self) -> Bool {\n        return lhs.id == rhs.id\n    }\n}\n\nstruct WindowSet<Window: WindowType> {\n    let windows: [LayoutWindow<Window>]\n    private let isWindowWithIDActive: (Window.WindowID) -> Bool\n    private let isWindowWithIDFloating: (Window.WindowID) -> Bool\n    private let windowForID: (Window.WindowID) -> Window?\n\n    init(\n        windows: [LayoutWindow<Window>],\n        isWindowWithIDActive: @escaping (Window.WindowID) -> Bool,\n        isWindowWithIDFloating: @escaping (Window.WindowID) -> Bool,\n        windowForID: @escaping (Window.WindowID) -> Window?\n    ) {\n        self.windows = windows\n        self.isWindowWithIDActive = isWindowWithIDActive\n        self.isWindowWithIDFloating = isWindowWithIDFloating\n        self.windowForID = windowForID\n    }\n\n    func isWindowActive(_ window: LayoutWindow<Window>) -> Bool {\n        return isWindowWithIDActive(window.id)\n    }\n\n    func isWindowFloating(_ window: LayoutWindow<Window>) -> Bool {\n        return isWindowWithIDFloating(window.id)\n    }\n\n    func perform(frameAssignment: FrameAssignment<Window>) {\n        guard let window = windowForID(frameAssignment.window.id) else {\n            return\n        }\n\n        guard isWindowWithIDActive(frameAssignment.window.id), !isWindowWithIDFloating(frameAssignment.window.id) else {\n            return\n        }\n\n        frameAssignment.perform(withWindow: window)\n    }\n}\n\nclass FrameAssignmentOperation<Window: WindowType>: Operation {\n    let frameAssignment: FrameAssignment<Window>\n    let windowSet: WindowSet<Window>\n\n    init(frameAssignment: FrameAssignment<Window>, windowSet: WindowSet<Window>) {\n        self.frameAssignment = frameAssignment\n        self.windowSet = windowSet\n        super.init()\n    }\n\n    override func main() {\n        guard !isCancelled else {\n            return\n        }\n\n        windowSet.perform(frameAssignment: frameAssignment)\n    }\n}\n\n/// Encapsulation of an assignment of a frame to a window.\nstruct FrameAssignment<Window: WindowType> {\n    /// The frame to apply to the window.\n    let frame: CGRect\n\n    /// The window that will be moved and sized.\n    let window: LayoutWindow<Window>\n\n    /// The frame of the screen being occupied.\n    let screenFrame: CGRect\n\n    /// The rules governing constraints to frame transforms\n    let resizeRules: ResizeRules\n\n    /// If `true`, then  window margins won't be applied\n    let disableWindowMargins: Bool\n\n    init(frame: CGRect, window: LayoutWindow<Window>, screenFrame: CGRect, resizeRules: ResizeRules) {\n        self.frame = frame\n        self.window =  window\n        self.screenFrame = screenFrame\n        self.resizeRules = resizeRules\n        self.disableWindowMargins = false\n    }\n\n    init(frame: CGRect, window: LayoutWindow<Window>, screenFrame: CGRect, resizeRules: ResizeRules, disableWindowMargins: Bool) {\n        self.frame = frame\n        self.window =  window\n        self.screenFrame = screenFrame\n        self.resizeRules = resizeRules\n        self.disableWindowMargins = disableWindowMargins\n    }\n\n    /// The final frame is the desired frame, but transformed to provide desired padding\n    var finalFrame: CGRect {\n        var ret = frame\n        let padding = floor(UserConfiguration.shared.windowMarginSize() / 2)\n\n        if UserConfiguration.shared.windowMargins() && !disableWindowMargins {\n            ret.origin.x += padding\n            ret.origin.y += padding\n            ret.size.width -= 2 * padding\n            ret.size.height -= 2 * padding\n        }\n\n        let windowMinimumWidth = UserConfiguration.shared.windowMinimumWidth()\n        let windowMinimumHeight = UserConfiguration.shared.windowMinimumHeight()\n\n        if windowMinimumWidth > ret.size.width {\n            ret.origin.x -= ((windowMinimumWidth - ret.size.width) / 2)\n            ret.size.width = windowMinimumWidth\n        }\n\n        if windowMinimumHeight > ret.size.height {\n            ret.origin.y -= ((windowMinimumHeight - ret.size.height) / 2)\n            ret.size.height = windowMinimumHeight\n        }\n\n        return ret\n    }\n\n    /**\n     Given a window frame and based on resizeRules, determine what the main pane ratio would be.\n     \n     This accounts for multiple main windows and primary vs non-primary being resized.\n     \n     - Parameters:\n        - windowFrame: The frame of the window to test ratio against.\n     \n     - Returns:\n     The estimate of the main pane ratio implied by how the frame would be transformed.\n     */\n    func impliedMainPaneRatio(windowFrame: CGRect) -> CGFloat {\n        let oldDimension = resizeRules.scaledDimension(frame, negatePadding: false)\n        let newDimension = resizeRules.scaledDimension(windowFrame, negatePadding: true)\n        let implied =  (newDimension / oldDimension) / resizeRules.scaleFactor\n        return resizeRules.isMain ? implied : 1 - implied\n    }\n\n    /// Perform the actual application of the frame to the window\n    func perform(withWindow window: Window) {\n        var finalFrame = self.finalFrame\n        var finalOrigin = finalFrame.origin\n\n        // If this is the focused window then we need to shift it to be on screen regardless of size\n        // We call this \"window peeking\" (this line here to aid in text search)\n        if window.isFocused() {\n            // Just resize the window first to see what the dimensions end up being\n            // Sometimes applications have internal window requirements that are not exposed to us directly\n            finalFrame.origin = window.frame().origin\n            DispatchQueue.main.sync {\n                window.setFrame(finalFrame, withThreshold: CGSize(width: 1, height: 1))\n            }\n\n            // With the real height we can update the frame to account for the current size\n            finalFrame.size = CGSize(\n                width: max(window.frame().width, finalFrame.width),\n                height: max(window.frame().height, finalFrame.height)\n            )\n            finalOrigin.x = max(screenFrame.minX, min(finalOrigin.x, screenFrame.maxX - finalFrame.size.width))\n            finalOrigin.y = max(screenFrame.minY, min(finalOrigin.y, screenFrame.maxY - finalFrame.size.height))\n        }\n\n        // Move the window to its final frame\n        finalFrame.origin = finalOrigin\n        DispatchQueue.main.sync {\n            window.setFrame(finalFrame, withThreshold: CGSize(width: 1, height: 1))\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/AppManager.swift",
    "content": "//\n//  relaunch.swift\n//  Amethyst\n//\n//  Created by Agustin Suarez on 2021-02-23.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Cocoa\n\nclass AppManager {\n    public static func relaunch() {\n        let executablePath = Bundle.main.executablePath! as NSString\n        let fileSystemRepresentedPath = executablePath.fileSystemRepresentation\n        let fileSystemPath = FileManager.default.string(withFileSystemRepresentation: fileSystemRepresentedPath, length: Int(strlen(fileSystemRepresentedPath)))\n        Process.launchedProcess(launchPath: fileSystemPath, arguments: [])\n        NSApp.terminate(self)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/FocusFollowsMouseManager.swift",
    "content": "//\n//  FocusFollowsMouseManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport Silica\nimport RxSwift\n\nprotocol FocusFollowsMouseManagerDelegate: AnyObject {\n    associatedtype Window: WindowType\n    typealias Screen = Window.Screen\n    func windows(onScreen screen: Screen) -> [Window]\n}\n\nclass FocusFollowsMouseManager<Delegate: FocusFollowsMouseManagerDelegate> {\n    typealias Window = Delegate.Window\n    typealias Screen = Window.Screen\n\n    weak var delegate: Delegate?\n\n    private var lastMouseFocusTime = Date.distantPast\n\n    private let userConfiguration: UserConfiguration\n    private let disposeBag = DisposeBag()\n\n    init(userConfiguration: UserConfiguration) {\n        self.userConfiguration = userConfiguration\n\n        // we want to observe changes to the focusFollowsMouse config, because mouse tracking has CPU cost\n        UserDefaults.standard.rx.observe(Bool.self, ConfigurationKey.focusFollowsMouse.rawValue)\n            .distinctUntilChanged { $0 == $1 }\n            .scan(nil) { [unowned self] existingHandler, followingIsDesired -> Any? in\n                if let handler = existingHandler {\n                    NSEvent.removeMonitor(handler)\n                }\n                if followingIsDesired! {\n                    return NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { [unowned self] event in\n                        self.focusWindowWithMouseMovedEvent(event)\n                    }\n                } else {\n                    return nil\n                }\n            }\n            .subscribe()\n            .disposed(by: disposeBag)\n    }\n\n    private func focusWindowWithMouseMovedEvent(_ event: NSEvent) {\n        guard userConfiguration.focusFollowsMouse() else {\n            log.warning(\"Subscribed to mouse move events that we are ignoring\")\n            return\n        }\n\n        guard let screen = Screen.availableScreens.first(where: { $0.frameIncludingDockAndMenu().contains(event.locationInWindow) }) else {\n            return\n        }\n\n        guard let windows = delegate?.windows(onScreen: screen) else {\n            return\n        }\n\n        var mousePoint = NSPointToCGPoint(event.locationInWindow)\n        mousePoint.y = Screen.globalHeight() - mousePoint.y + screen.frameIncludingDockAndMenu().origin.y\n\n        if let focusedWindow = Window.currentlyFocused() {\n            // If the point is already in the frame of the focused window do nothing.\n            guard !focusedWindow.frame().contains(mousePoint) else {\n                return\n            }\n        }\n\n        guard let topWindow = WindowsInformation.topWindowForScreenAtPoint(mousePoint, withWindows: windows) else {\n            return\n        }\n\n        self.lastMouseFocusTime = Date()\n\n        topWindow.focus()\n    }\n\n    func recentlyTriggeredFocusFollowsMouse() -> Bool {\n        return Date().timeIntervalSince(lastMouseFocusTime) < 0.5\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/FocusTransitionCoordinator.swift",
    "content": "//\n//  FocusTransitionCoordinator.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/24/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport Silica\n\nenum FocusTransition<Window: WindowType> {\n    typealias Screen = Window.Screen\n    case focusWindow(_ window: Window)\n    case focusScreen(_ screen: Screen)\n}\n\nprotocol FocusTransitionTarget: AnyObject {\n    associatedtype Application: ApplicationType\n    typealias Window = Application.Window\n    typealias Screen = Window.Screen\n\n    func executeTransition(_ transition: FocusTransition<Window>)\n\n    func lastFocusedWindow(on screen: Screen) -> Window?\n    func screen(at index: Int) -> Screen?\n    func windows(onScreen screen: Screen) -> [Window]\n    func nextWindowIDClockwise(on screen: Screen) -> Window.WindowID?\n    func nextWindowIDCounterClockwise(on screen: Screen) -> Window.WindowID?\n    func nextScreenIndexClockwise(from screen: Screen) -> Int\n    func nextScreenIndexCounterClockwise(from screen: Screen) -> Int\n}\n\nclass FocusTransitionCoordinator<Target: FocusTransitionTarget> {\n    typealias Window = Target.Window\n    typealias Screen = Window.Screen\n\n    weak var target: Target?\n\n    private let userConfiguration: UserConfiguration\n    private let focusFollowsMouseManager: FocusFollowsMouseManager<FocusTransitionCoordinator<Target>>\n\n    init(userConfiguration: UserConfiguration) {\n        self.userConfiguration = userConfiguration\n        self.focusFollowsMouseManager = FocusFollowsMouseManager(userConfiguration: userConfiguration)\n        self.focusFollowsMouseManager.delegate = self\n    }\n\n    func moveFocusCounterClockwise() {\n        guard let focusedWindow = Window.currentlyFocused() else {\n            focusScreen(at: 0)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {\n            return\n        }\n\n        let windowToFocus = { () -> Window in\n            if let nextWindowID = self.target?.nextWindowIDCounterClockwise(on: screen) {\n                let windowToFocusIndex = windows.firstIndex { $0.id() == nextWindowID } ?? 0\n                return windows[windowToFocusIndex]\n            } else {\n                let windowIndex = windows.firstIndex(of: focusedWindow) ?? 0\n                let windowToFocusIndex = (windowIndex == 0 ? windows.count - 1 : windowIndex - 1)\n                return windows[windowToFocusIndex]\n            }\n        }()\n\n        windowToFocus.focus()\n    }\n\n    func moveFocusClockwise() {\n        guard let focusedWindow = Window.currentlyFocused() else {\n            focusScreen(at: 0)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {\n            return\n        }\n\n        let windowToFocus = { () -> Window in\n            if let nextWindowID = target?.nextWindowIDClockwise(on: screen) {\n                let windowToFocusIndex = windows.firstIndex { $0.id() == nextWindowID } ?? 0\n                return windows[windowToFocusIndex]\n            } else {\n                let windowIndex = windows.firstIndex(of: focusedWindow) ?? windows.count - 1\n                let windowToFocusIndex = (windowIndex + 1) % windows.count\n                return windows[windowToFocusIndex]\n            }\n        }()\n\n        windowToFocus.focus()\n    }\n\n    func moveFocusToMain() {\n        guard let focusedWindow = Window.currentlyFocused() else {\n            focusScreen(at: 0)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {\n            return\n        }\n\n        if focusedWindow.id() == windows[0].id() {\n            (target?.lastFocusedWindow(on: screen) ?? windows[0]).focus()\n        } else {\n            windows[0].focus()\n        }\n    }\n\n    func focusScreen(at screenIndex: Int) {\n        guard let screen = target?.screen(at: screenIndex) else {\n            return\n        }\n\n        // Do nothing if the screen is already focused\n        if let focusedWindow = Window.currentlyFocused(), let focusedScreen = focusedWindow.screen(), focusedScreen == screen {\n            return\n        }\n\n        // If the previous focus has been tracked, then focus the window that had the focus before.\n        if let previouslyFocused = target?.lastFocusedWindow(on: screen), previouslyFocused.isOnScreen() {\n            target?.executeTransition(.focusWindow(previouslyFocused))\n            return\n        }\n\n        // If there are no windows on the screen focus the screen directly\n        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {\n            target?.executeTransition(.focusScreen(screen))\n            return\n        }\n\n        // Otherwise find the topmost window on the screen\n        let screenCenter = NSPointToCGPoint(NSPoint(\n            x: screen.frameIncludingDockAndMenu().midX,\n            y: screen.frameIncludingDockAndMenu().midY\n        ))\n\n        // If there is no window at that point just focus the screen directly\n        guard let topWindow = WindowsInformation.topWindowForScreenAtPoint(screenCenter, withWindows: windows) ?? windows.first else {\n            target?.executeTransition(.focusScreen(screen))\n            return\n        }\n\n        // Otherwise focus the topmost window\n        target?.executeTransition(.focusWindow(topWindow))\n    }\n\n    func moveFocusScreenCounterClockwise() {\n        guard let focusedScreen = Window.currentlyFocused()?.screen() else {\n            return\n        }\n\n        guard let nextScreenIndex = target?.nextScreenIndexCounterClockwise(from: focusedScreen) else {\n            return\n        }\n\n        focusScreen(at: nextScreenIndex)\n    }\n\n    func moveFocusScreenClockwise() {\n        guard let focusedScreen = Window.currentlyFocused()?.screen() else {\n            return\n        }\n\n        guard let screenIndex = target?.nextScreenIndexClockwise(from: focusedScreen) else {\n            return\n        }\n\n        focusScreen(at: screenIndex)\n    }\n\n    func recentlyTriggeredFocusFollowsMouse() -> Bool {\n        return focusFollowsMouseManager.recentlyTriggeredFocusFollowsMouse()\n    }\n}\n\nextension FocusTransitionCoordinator: FocusFollowsMouseManagerDelegate {\n    func windows(onScreen screen: Screen) -> [Window] {\n        return target?.windows(onScreen: screen) ?? []\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/HotKeyRegistrar.swift",
    "content": "//\n//  HotKeyRegistrar.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport KeyboardShortcuts\nimport MASShortcut\n\nprotocol HotKeyRegistrar {\n    func registerHotKey(with string: String?, modifiers: AMModifierFlags?, handler: @escaping () -> Void, defaultsKey: String, override: Bool)\n}\n\nextension HotKeyManager: HotKeyRegistrar {\n    func registerHotKey(with string: String?, modifiers: AMModifierFlags?, handler: @escaping () -> Void, defaultsKey: String, override: Bool) {\n        let name = KeyboardShortcuts.Name(defaultsKey)\n        let migrationKey = \"migrated-\\(name.rawValue)\"\n        let isMigrated = UserDefaults.standard.bool(forKey: migrationKey)\n\n        defer {\n            UserDefaults.standard.set(true, forKey: migrationKey)\n            KeyboardShortcuts.onKeyUp(for: name, action: handler)\n        }\n\n        if override {\n            MASShortcutBinder.shared().breakBinding(withDefaultsKey: defaultsKey)\n            UserDefaults.standard.removeObject(forKey: defaultsKey)\n            KeyboardShortcuts.setShortcut(nil, for: name)\n        }\n\n        guard KeyboardShortcuts.getShortcut(for: name) == nil && (!isMigrated || override) else {\n            return\n        }\n\n        if let value = UserDefaults.standard.object(forKey: defaultsKey),\n           let shortcut = ValueTransformer(forName: .keyedUnarchiveFromDataTransformerName)?.transformedValue(value) as? MASShortcut {\n            let shortcutKey = KeyboardShortcuts.Key(rawValue: shortcut.keyCode)\n            let newShortcut = KeyboardShortcuts.Shortcut(shortcutKey, modifiers: shortcut.modifierFlags)\n            // Keeping the old shortcuts in defaults for now to prevent data loss\n//            UserDefaults.standard.removeObject(forKey: defaultsKey)\n            KeyboardShortcuts.setShortcut(newShortcut, for: name)\n            return\n        }\n\n        if let string = string, let modifiers = modifiers {\n            if let keyCodes = stringToKeyCodes[string.lowercased()], !keyCodes.isEmpty {\n                let shortcutKey = KeyboardShortcuts.Key(rawValue: keyCodes[0])\n                let shortcut = KeyboardShortcuts.Shortcut(shortcutKey, modifiers: modifiers)\n                KeyboardShortcuts.setShortcut(shortcut, for: name)\n            } else {\n                log.warning(\"String \\\"\\(string)\\\" does not map to any keycodes\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/LayoutType.swift",
    "content": "//\n//  LayoutManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\n\nextension FileManager {\n    func layoutsDirectory() throws -> URL {\n        let applicationSupportDirectory = try FileManager.default.url(\n            for: .applicationSupportDirectory,\n            in: .userDomainMask,\n            appropriateFor: nil,\n            create: true\n        )\n        let layoutsDirectory = applicationSupportDirectory\n            .appendingPathComponent(\"Amethyst\", isDirectory: true)\n            .appendingPathComponent(\"Layouts\", isDirectory: true)\n\n        if !FileManager.default.fileExists(atPath: layoutsDirectory.path, isDirectory: nil) {\n            try FileManager.default.createDirectory(\n                at: layoutsDirectory,\n                withIntermediateDirectories: true,\n                attributes: nil\n            )\n        }\n\n        return layoutsDirectory\n    }\n\n    func layoutFile(key: String) throws -> URL {\n        let layoutsDirectory = try self.layoutsDirectory()\n        return layoutsDirectory.appendingPathComponent(\"\\(key).js\")\n    }\n}\n\nenum LayoutType<Window: WindowType> {\n    enum Error: Swift.Error {\n        case unknownLayout\n    }\n\n    case tall\n    case tallRight\n    case wide\n    case twoPane\n    case twoPaneRight\n    case threeColumnLeft\n    case threeColumnMiddle\n    case threeColumnRight\n    case fourColumnLeft\n    case fourColumnRight\n    case fullscreen\n    case column\n    case row\n    case floating\n    case widescreenTallLeft\n    case widescreenTallRight\n    case binarySpacePartitioning\n\n    case custom(key: String)\n\n    static var standardLayouts: [LayoutType<Window>] {\n        return [\n            .tall,\n            .tallRight,\n            .wide,\n            .twoPane,\n            .twoPaneRight,\n            .threeColumnLeft,\n            .threeColumnMiddle,\n            .threeColumnRight,\n            .fourColumnLeft,\n            .fourColumnRight,\n            .fullscreen,\n            .column,\n            .row,\n            .floating,\n            .widescreenTallLeft,\n            .widescreenTallRight,\n            .binarySpacePartitioning\n        ]\n    }\n\n    var key: String {\n        switch self {\n        case .tall:\n            return \"tall\"\n        case .tallRight:\n            return \"tall-right\"\n        case .wide:\n            return \"wide\"\n        case .twoPane:\n            return \"two-pane\"\n        case .twoPaneRight:\n            return \"two-pane-right\"\n        case .threeColumnLeft:\n            return \"3column-left\"\n        case .threeColumnMiddle:\n            return \"middle-wide\"\n        case .threeColumnRight:\n            return \"3column-right\"\n        case .fourColumnLeft:\n            return \"4column-left\"\n        case .fourColumnRight:\n            return \"4column-right\"\n        case .fullscreen:\n            return \"fullscreen\"\n        case .column:\n            return \"column\"\n        case .row:\n            return \"row\"\n        case .floating:\n            return \"floating\"\n        case .widescreenTallLeft:\n            return \"widescreen-tall\"\n        case .widescreenTallRight:\n            return \"widescreen-tall-right\"\n        case .binarySpacePartitioning:\n            return \"bsp\"\n        case .custom(let key):\n            return key\n        }\n    }\n\n    var layoutClass: Layout<Window>.Type {\n        switch self {\n        case .tall:\n            return TallLayout<Window>.self\n        case .tallRight:\n            return TallRightLayout<Window>.self\n        case .wide:\n            return WideLayout<Window>.self\n        case .twoPane:\n            return TwoPaneLayout<Window>.self\n        case .twoPaneRight:\n            return TwoPaneRightLayout<Window>.self\n        case .threeColumnLeft:\n            return ThreeColumnLeftLayout<Window>.self\n        case .threeColumnMiddle:\n            return ThreeColumnMiddleLayout<Window>.self\n        case .threeColumnRight:\n            return ThreeColumnRightLayout<Window>.self\n        case .fourColumnLeft:\n            return FourColumnLeftLayout<Window>.self\n        case .fourColumnRight:\n            return FourColumnRightLayout<Window>.self\n        case .fullscreen:\n            return FullscreenLayout<Window>.self\n        case .column:\n            return ColumnLayout<Window>.self\n        case .row:\n            return RowLayout<Window>.self\n        case .floating:\n            return FloatingLayout<Window>.self\n        case .widescreenTallLeft:\n            return WidescreenTallLayoutLeft<Window>.self\n        case .widescreenTallRight:\n            return WidescreenTallLayoutRight<Window>.self\n        case .binarySpacePartitioning:\n            return BinarySpacePartitioningLayout<Window>.self\n        case .custom:\n            return CustomLayout<Window>.self\n        }\n    }\n\n    static func from(key: String) -> LayoutType<Window> {\n        switch key {\n        case \"tall\":\n            return .tall\n        case \"tall-right\":\n            return .tallRight\n        case \"wide\":\n            return .wide\n        case \"two-pane\":\n            return .twoPane\n        case \"two-pane-right\":\n            return .twoPaneRight\n        case \"3column-left\":\n            return .threeColumnLeft\n        case \"middle-wide\":\n            return .threeColumnMiddle\n        case \"3column-right\":\n            return .threeColumnRight\n        case \"4column-left\":\n            return .fourColumnLeft\n        case \"4column-right\":\n            return .fourColumnRight\n        case \"fullscreen\":\n            return .fullscreen\n        case \"column\":\n            return .column\n        case \"row\":\n            return .row\n        case \"floating\":\n            return .floating\n        case \"widescreen-tall\":\n            return .widescreenTallLeft\n        case \"widescreen-tall-right\":\n            return .widescreenTallRight\n        case \"bsp\":\n            return .binarySpacePartitioning\n        default:\n            return .custom(key: key)\n        }\n    }\n\n    static func layoutForKey(_ layoutKey: String) -> Layout<Window>? {\n        let type = LayoutType<Window>.from(key: layoutKey)\n\n        guard case .custom = type else {\n            return type.layoutClass.init()\n        }\n\n        do {\n            let layoutFile = try FileManager.default.layoutFile(key: layoutKey)\n\n            guard FileManager.default.fileExists(atPath: layoutFile.path) else {\n                return nil\n            }\n\n            return CustomLayout<Window>(key: layoutKey, fileURL: layoutFile)\n        } catch {\n            return nil\n        }\n    }\n\n    static func layoutNameForKey(_ layoutKey: String) -> String? {\n        let type = LayoutType<Window>.from(key: layoutKey)\n\n        guard case .custom = type else {\n            return type.layoutClass.layoutName\n        }\n\n        return layoutForKey(layoutKey)?.layoutName\n    }\n\n    static func standardLayoutClasses() -> [Layout<Window>.Type] {\n        return standardLayouts.compactMap { $0.layoutClass }\n    }\n\n    // Returns a list of (key, name) pairs\n    static func availableLayoutStrings() -> [(key: String, name: String)] {\n        var layoutTypes = standardLayouts\n            .compactMap { $0.layoutClass }\n            .map { ($0.layoutKey, $0.layoutName) }\n\n        do {\n            let layoutsDirectory = try FileManager.default.layoutsDirectory()\n            let customLayoutFiles = try FileManager.default.contentsOfDirectory(\n                at: layoutsDirectory,\n                includingPropertiesForKeys: nil,\n                options: []\n            ).filter { $0.pathExtension == \"js\" }\n\n            let customLayouts = customLayoutFiles\n                .map { $0.deletingPathExtension().lastPathComponent }\n                .map { ($0, layoutNameForKey($0)!) }\n            layoutTypes.append(contentsOf: customLayouts)\n        } catch {\n            log.error(\"failed to parse custom layouts\")\n        }\n\n        return layoutTypes\n    }\n\n    static func layoutsWithConfiguration(_ userConfiguration: UserConfiguration) -> [Layout<Window>] {\n        let layoutKeys: [String] = userConfiguration.layoutKeys()\n        let layouts = layoutKeys.map { layoutKey -> Layout<Window>? in\n            guard let layout = LayoutType.layoutForKey(layoutKey) else {\n                log.warning(\"Unrecognized layout key \\(layoutKey)\")\n                return nil\n            }\n\n            return layout\n        }\n\n        return layouts.compactMap { $0 }\n    }\n\n    static func encoded(layout: Layout<Window>) throws -> Data {\n        return try JSONEncoder().encode(layout)\n    }\n\n    static func decoded(data: Data, key: String) throws -> Layout<Window> {\n        let layoutType = LayoutType<Window>.from(key: key)\n        let decoder = JSONDecoder()\n        return try decoder.decode(layoutType.layoutClass, from: data)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/LogManager.swift",
    "content": "//\n//  LogManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/19/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport SwiftyBeaver\n\nlet log = SwiftyBeaver.self\n"
  },
  {
    "path": "Amethyst/Managers/ScreenManager.swift",
    "content": "//\n//  ScreenManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/23/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\n/// Information about a layout for display in menus\nstruct LayoutMenuItemInfo {\n    let key: String\n    let name: String\n    let isSelected: Bool\n}\n\nprotocol ScreenManagerDelegate: AnyObject {\n    associatedtype Window: WindowType\n    func applyWindowLimit(forScreenManager screenManager: ScreenManager<Self>, minimizingIn range: (_ windowCount: Int) -> Range<Int>)\n    func activeWindowSet(forScreenManager screenManager: ScreenManager<Self>) -> WindowSet<Window>\n    func onReflowInitiation()\n    func onReflowCompletion()\n}\n\nfinal class ScreenManager<Delegate: ScreenManagerDelegate>: NSObject, Codable {\n    typealias Window = Delegate.Window\n    typealias Screen = Window.Screen\n\n    enum CodingKeys: String, CodingKey {\n        case layoutsBySpaceUUID\n    }\n\n    weak var delegate: Delegate?\n\n    private(set) var screen: Screen?\n    private(set) var space: Space?\n\n    /// The last window that has been focused on the screen. This value is updated by the notification observations in\n    /// `ObserveApplicationNotifications`.\n    private(set) var lastFocusedWindow: Window?\n    private let userConfiguration: UserConfiguration\n\n    private let reflowOperationDispatchQueue = DispatchQueue(\n        label: \"ScreenManager.reflowOperationQueue\",\n        qos: .utility,\n        attributes: [],\n        autoreleaseFrequency: .inherit,\n        target: nil\n    )\n    private let reflowOperationQueue = OperationQueue()\n\n    private var layouts: [Layout<Window>] = []\n    private var currentLayoutIndexBySpaceUUID: [String: Int] = [:]\n    private var layoutsBySpaceUUID: [String: [Layout<Window>]] = [:]\n    private var currentLayoutIndex: Int = 0\n    var previousLayoutKey: String?\n    var currentLayout: Layout<Window>? {\n        guard !layouts.isEmpty else {\n            return nil\n        }\n        return layouts[currentLayoutIndex]\n    }\n\n    /// Returns layout info for all layouts in this screen manager, including selection state\n    var layoutsInfo: [LayoutMenuItemInfo] {\n        return layouts.enumerated().map { index, layout in\n            LayoutMenuItemInfo(\n                key: layout.layoutKey,\n                name: layout.layoutName,\n                isSelected: index == currentLayoutIndex\n            )\n        }\n    }\n\n    private let layoutNameWindowController: LayoutNameWindowController\n\n    init(screen: Screen, delegate: Delegate, userConfiguration: UserConfiguration) {\n        self.screen = screen\n        self.delegate = delegate\n        self.userConfiguration = userConfiguration\n\n        layoutNameWindowController = LayoutNameWindowController(windowNibName: \"LayoutNameWindow\")\n\n        super.init()\n\n        layouts = LayoutType.layoutsWithConfiguration(userConfiguration)\n\n        reflowOperationQueue.underlyingQueue = reflowOperationDispatchQueue\n    }\n\n    init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        let layoutsBySpaceUUID = try values.decode([String: [[String: Data]]].self, forKey: .layoutsBySpaceUUID)\n\n        self.userConfiguration = UserConfiguration.shared\n        self.layoutsBySpaceUUID = try layoutsBySpaceUUID.mapValues { keyedLayouts -> [Layout<Window>] in\n            return try ScreenManager<Delegate>.decodedLayouts(from: keyedLayouts, userConfiguration: UserConfiguration.shared)\n        }\n\n        layoutNameWindowController = LayoutNameWindowController(windowNibName: \"LayoutNameWindow\")\n    }\n\n    /**\n     Takes the list of layouts and inserts decoded layouts where appropriate.\n\n     - Parameters:\n        - encodedLayouts: A list of encoded layouts to be restored.\n        - userConfiguration: User configuration defining the list of layouts.\n     */\n    static func decodedLayouts(from encodedLayouts: [[String: Data]], userConfiguration: UserConfiguration) throws -> [Layout<Window>] {\n        let layouts: [Layout<Window>] = LayoutType.layoutsWithConfiguration(userConfiguration)\n        var decodedLayouts: [Layout<Window>] = encodedLayouts.compactMap { layout in\n            guard let keyData = layout[\"key\"], let key = String(data: keyData, encoding: .utf8) else {\n                return nil\n            }\n\n            guard let data = layout[\"data\"] else {\n                return nil\n            }\n\n            do {\n                return try LayoutType<Window>.decoded(data: data, key: key)\n            } catch {\n                log.error(\"Failed to to decode layout: \\(key)\")\n            }\n\n            return nil\n        }\n\n        // Yes this is quadratic, but if your layout list is long enough for that to be significant what are you even doing?\n        return layouts.map { layout -> Layout<Window> in\n            guard let decodedLayoutIndex = decodedLayouts.firstIndex(where: { $0.layoutKey == layout.layoutKey }) else {\n                return layout\n            }\n\n            return decodedLayouts.remove(at: decodedLayoutIndex)\n        }\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var values = encoder.container(keyedBy: CodingKeys.self)\n        let layoutsBySpaceUUID = try self.layoutsBySpaceUUID.mapValues { layouts in\n            return try layouts.map { layout -> [String: Data] in\n                let layoutKey = layout.layoutKey.data(using: .utf8)!\n                let encodedLayout = try LayoutType.encoded(layout: layout)\n                return [\"key\": layoutKey, \"data\": encodedLayout]\n            }\n        }\n        try values.encode(layoutsBySpaceUUID, forKey: .layoutsBySpaceUUID)\n    }\n\n    func updateScreen(to screen: Screen) {\n        self.screen = screen\n    }\n\n    func updateSpace(to space: Space) {\n        if let currentSpace = self.space {\n            currentLayoutIndexBySpaceUUID[currentSpace.uuid] = currentLayoutIndex\n        }\n\n        self.space = space\n\n        setCurrentLayoutIndex(currentLayoutIndexBySpaceUUID[space.uuid] ?? 0, changingSpace: true)\n\n        if let layouts = layoutsBySpaceUUID[space.uuid] {\n            self.layouts = layouts\n        } else {\n            self.layouts = LayoutType.layoutsWithConfiguration(userConfiguration)\n            layoutsBySpaceUUID[space.uuid] = layouts\n        }\n    }\n\n    func distributeEvent(_ change: Change<Window>) {\n        switch change {\n        case let .add(window: window):\n            lastFocusedWindow = window\n        case let .focusChanged(window):\n            lastFocusedWindow = window\n        case let .remove(window):\n            if lastFocusedWindow == window {\n                lastFocusedWindow = nil\n            }\n        case .windowSwap, .applicationActivate, .applicationDeactivate, .spaceChange, .layoutChange, .tabChange, .none, .unknown:\n            break\n        }\n\n        log.debug(\"Screen: \\(screen?.screenID() ?? \"unknown\") reflow -- Window Change: \\(change)\")\n\n        guard let space, let layouts = layoutsBySpaceUUID[space.uuid] else {\n            log.warning(\"Trying to distribute an event to a screen with no space\")\n            return\n        }\n\n        for layout in layouts {\n            if let layout = layout as? StatefulLayout {\n                layout.updateWithChange(change)\n            }\n        }\n    }\n\n    func setNeedsReflow() {\n        reflowOperationQueue.cancelAllOperations()\n\n        log.debug(\"Screen: \\(screen?.screenID() ?? \"unknown\") reflow\")\n\n        DispatchQueue.main.async {\n            self.minimizeWindows()\n            self.reflow()\n        }\n    }\n\n    private func minimizeWindows() {\n        let mainPaneCount = (currentLayout as? PanedLayout)?.mainPaneCount ?? 0\n\n        guard UserConfiguration.shared.tilingEnabled, let windowLimit = UserConfiguration.shared.windowMaxCount() else {\n            return\n        }\n        let shouldInsertAtFront = UserConfiguration.shared.sendNewWindowsToMainPane()\n        delegate?.applyWindowLimit(forScreenManager: self, minimizingIn: { windowCount in\n            if windowLimit > windowCount {\n                // Not enough windows to minimize.\n                return 0 ..< 0\n            }\n            if !(currentLayout is PanedLayout) {\n                // Minimize from the back, for layouts like floating/fullscreen.\n                if shouldInsertAtFront {\n                    return windowLimit ..< windowCount\n                } else {\n                    return 0 ..< windowCount - windowLimit\n                }\n            }\n            if windowLimit <= mainPaneCount {\n                // Don't minimize main panes. This allowing varying main pane count to pin windows.\n                guard windowCount >= mainPaneCount else {return 0 ..< 0}\n                return mainPaneCount ..< windowCount\n            }\n            // Minimize the oldest non-main panes.\n            if shouldInsertAtFront {\n                return windowLimit ..< windowCount\n            } else {\n                return mainPaneCount ..< windowCount + mainPaneCount - windowLimit\n            }\n        })\n    }\n\n    private func reflow() {\n        guard let screen = screen else {\n            return\n        }\n\n        guard userConfiguration.tilingEnabled, space?.type == CGSSpaceTypeUser else {\n            return\n        }\n\n        // During rapid Space transitions, activation/focus notifications can arrive before\n        // this screen manager updates its tracked Space. Skip reflow if state is stale.\n        guard let currentSpace = CGSpacesInfo<Window>.currentSpaceForScreen(screen), currentSpace.id == space?.id else {\n            return\n        }\n\n        guard let windows = delegate?.activeWindowSet(forScreenManager: self) else {\n            return\n        }\n\n        guard let layout = currentLayout, let frameAssignments = layout.frameAssignments(windows, on: screen) else {\n            return\n        }\n\n        // TODO: fix mff\n//        let mouseFollowsFocus = userConfiguration.mouseFollowsFocus()\n\n        let completeOperation = BlockOperation()\n\n        // The complete operation should execute the completion delegate call\n        completeOperation.addExecutionBlock { [unowned completeOperation, weak self] in\n            if completeOperation.isCancelled {\n                return\n            }\n\n            DispatchQueue.main.async {\n                self?.delegate?.onReflowCompletion()\n                // TODO: fix mff\n//                if mouseFollowsFocus {\n//                    if case .windowSwap(let window, _) = event {\n//                        window.focus()\n//                    }\n//                }\n            }\n        }\n\n        // The completion should be dependent on all assignments finishing\n        frameAssignments.forEach { completeOperation.addDependency($0) }\n\n        // Start the operation\n        delegate?.onReflowInitiation()\n        reflowOperationQueue.addOperations(frameAssignments, waitUntilFinished: false)\n        reflowOperationQueue.addOperation(completeOperation)\n    }\n\n    func updateCurrentLayout(_ updater: (Layout<Window>) -> Void) {\n        guard let layout = currentLayout else {\n            return\n        }\n        updater(layout)\n        setNeedsReflow()\n    }\n\n    func cycleLayoutForward() {\n        setCurrentLayoutIndex((currentLayoutIndex + 1) % layouts.count)\n        setNeedsReflow()\n    }\n\n    func cycleLayoutBackward() {\n        setCurrentLayoutIndex((currentLayoutIndex == 0 ? layouts.count : currentLayoutIndex) - 1)\n        setNeedsReflow()\n    }\n\n    func selectLayout(_ layoutString: String) {\n        guard let currentLayoutKey = currentLayout?.layoutKey else {\n            return\n        }\n\n        let nextLayoutKey = currentLayoutKey == layoutString ? previousLayoutKey : layoutString\n\n        guard let layoutIndex = layouts.firstIndex(where: { $0.layoutKey == nextLayoutKey }) else {\n            return\n        }\n\n        setCurrentLayoutIndex(layoutIndex)\n        setNeedsReflow()\n        previousLayoutKey = currentLayoutKey\n    }\n\n    private func setCurrentLayoutIndex(_ index: Int, changingSpace: Bool = false) {\n        guard (0..<layouts.count).contains(index) else {\n            return\n        }\n\n        currentLayoutIndex = index\n\n        guard !changingSpace || userConfiguration.enablesLayoutHUDOnSpaceChange() else {\n            return\n        }\n\n        DispatchQueue.main.async {\n            self.displayLayoutHUD()\n        }\n    }\n\n    func shrinkMainPane() {\n        guard let panedLayout = currentLayout as? PanedLayout else {\n            return\n        }\n        panedLayout.shrinkMainPane()\n    }\n\n    func expandMainPane() {\n        guard let panedLayout = currentLayout as? PanedLayout else {\n            return\n        }\n        panedLayout.expandMainPane()\n    }\n\n    func nextWindowIDCounterClockwise() -> Window.WindowID? {\n        guard let layout = currentLayout as? StatefulLayout else {\n            return nil\n        }\n\n        return layout.nextWindowIDCounterClockwise()\n    }\n\n    func nextWindowIDClockwise() -> Window.WindowID? {\n        guard let statefulLayout = currentLayout as? StatefulLayout else {\n            return nil\n        }\n\n        return statefulLayout.nextWindowIDClockwise()\n    }\n\n    func displayLayoutHUD() {\n        guard userConfiguration.enablesLayoutHUD(), space?.type == CGSSpaceTypeUser else {\n            return\n        }\n\n        let currentLayoutName = currentLayout.flatMap({ $0.layoutName }) ?? \"None\"\n        let currentLayoutDescription = currentLayout?.layoutDescription ?? \"\"\n\n        displayCustomHUD(title: currentLayoutName, description: currentLayoutDescription)\n    }\n\n    @objc func hideLayoutHUD(_ sender: AnyObject) {\n        layoutNameWindowController.close()\n    }\n\n    func displayCustomHUD(title: String, description: String = \"\") {\n        guard let screen = screen else {\n            return\n        }\n\n        guard space?.type == CGSSpaceTypeUser else {\n            return\n        }\n\n        defer {\n            NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(hideLayoutHUD(_:)), object: nil)\n            perform(#selector(hideLayoutHUD(_:)), with: nil, afterDelay: 0.6)\n        }\n\n        guard let layoutNameWindow = layoutNameWindowController.window as? LayoutNameWindow else {\n            return\n        }\n\n        // Use new displayNotification method with dynamic sizing\n        layoutNameWindow.displayNotification(title: title, description: description)\n\n        // Center the window after resizing\n        let screenFrame = screen.frame()\n        let screenCenter = CGPoint(x: screenFrame.midX, y: screenFrame.midY)\n        let windowOrigin = CGPoint(\n            x: screenCenter.x - layoutNameWindow.frame.width / 2.0,\n            y: screenCenter.y - layoutNameWindow.frame.height / 2.0\n        )\n        layoutNameWindow.setFrameOrigin(NSPointFromCGPoint(windowOrigin))\n\n        layoutNameWindowController.showWindow(self)\n    }\n}\n\nextension ScreenManager: Comparable {\n    static func < (lhs: ScreenManager<Delegate>, rhs: ScreenManager<Delegate>) -> Bool {\n        guard let lhsScreen = lhs.screen, let rhsScreen = rhs.screen else {\n            return false\n        }\n\n        let originX1 = lhsScreen.frameWithoutDockOrMenu().origin.x\n        let originX2 = rhsScreen.frameWithoutDockOrMenu().origin.x\n\n        return originX1 < originX2\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/Screens.swift",
    "content": "//\n//  Screens.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\nextension WindowManager {\n    class Screens: Codable {\n        enum CodingKeys: String, CodingKey {\n            case screenManagersCache\n        }\n\n        private(set) var screenManagers: [ScreenManager<WindowManager<Application>>] = []\n        private var screenManagersCache: [String: ScreenManager<WindowManager<Application>>] = [:]\n\n        init() {}\n\n        func updateSpaces() {\n            guard let screensInfo = CGScreensInfo<Window>() else {\n                return\n            }\n\n            if Screen.screensHaveSeparateSpaces {\n                for screenDictionary in screensInfo.descriptions {\n                    guard let screenID = screenDictionary[\"Display Identifier\"].string else {\n                        log.error(\"Could not identify screen with info: \\(screenDictionary)\")\n                        continue\n                    }\n\n                    guard let screenManager = screenManagersCache[screenID] else {\n                        log.error(\"Screen with identifier not managed: \\(screenID)\")\n                        continue\n                    }\n\n                    let space = CGSpacesInfo<Window>.space(fromScreenDescription: screenDictionary)\n\n                    guard screenManager.space != space else {\n                        continue\n                    }\n\n                    screenManager.updateSpace(to: space)\n                }\n            } else {\n                for screenManager in screenManagers {\n                    let space = CGSpacesInfo<Window>.space(fromScreenDescription: screensInfo.descriptions[0])\n\n                    guard screenManager.space != space else {\n                        continue\n                    }\n\n                    screenManager.updateSpace(to: space)\n                }\n            }\n        }\n\n        func focusedScreenManager() -> ScreenManager<WindowManager<Application>>? {\n            guard let focusedWindow = Window.currentlyFocused() else {\n                return nil\n            }\n            return screenManagers.first { $0.screen?.screenID() == focusedWindow.screen()?.screenID() }\n        }\n\n        func updateScreens(windowManager: WindowManager) {\n            var screenManagers: [ScreenManager<WindowManager<Application>>] = []\n\n            for screen in Screen.availableScreens {\n                guard let screenID = screen.screenID() else {\n                    continue\n                }\n\n                let screenManager = screenManagersCache[screenID] ?? ScreenManager<WindowManager<Application>>(\n                    screen: screen,\n                    delegate: windowManager,\n                    userConfiguration: UserConfiguration.shared\n                )\n                screenManager.delegate = windowManager\n                screenManager.updateScreen(to: screen)\n\n                screenManagersCache[screenID] = screenManager\n\n                screenManagers.append(screenManager)\n            }\n\n            // Window managers are sorted by screen position along the x-axis.\n            // See `ScreenManager`'s `Comparable` conformance\n            self.screenManagers = screenManagers.sorted()\n\n            updateSpaces()\n            markAllScreensForReflow()\n        }\n\n        func distributeEventToScreen(_ screen: Screen, change: Change<Window>) {\n            screenManagers\n                .filter { $0.screen?.screenID() == screen.screenID() }\n                .forEach { screenManager in\n                    screenManager.distributeEvent(change)\n                }\n        }\n\n        func distributeEventToAllScreens(change: Change<Window>) {\n            for screenManager in screenManagers {\n                screenManager.distributeEvent(change)\n            }\n        }\n\n        func markScreenForReflow(_ screen: Screen) {\n            screenManagers\n                .filter { $0.screen?.screenID() == screen.screenID() }\n                .forEach { screenManager in\n                    screenManager.setNeedsReflow()\n                }\n        }\n\n        func markAllScreensForReflow() {\n            for screenManager in screenManagers {\n                screenManager.setNeedsReflow()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/WindowManager.swift",
    "content": "//\n//  WindowManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/14/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\nimport Carbon\nimport Foundation\nimport RxSwift\nimport Silica\nimport SwiftyJSON\n\nenum TrackingError: Error {\n    case unreliableFloating\n    case unknownScreen\n    case unknownSpace\n    case alreadyTracked\n}\n\n/**\n The tolerant interval between the click and the application of a mouse move from focus.\n \n - Note:\n \n At the time of the check we confirm that the mouse is not _currently_ clicked. However, it is possible that the click happened faster than the focus notification could be processed so that when we process the focus the mouse is no longer clicked. In this case we could incorrectly move the mouse to the center of the focused window.\n \n This value is an approximation of the time between a fast click and the focus event being processed. For values larger than this we would expect the mouse to still be clicked.\n */\nprivate let mouseMoveClickSpeedTolerance: TimeInterval = 0.3\n\nfinal class WindowManager<Application: ApplicationType>: NSObject, Codable {\n    typealias Window = Application.Window\n    typealias Screen = Window.Screen\n\n    struct PendingEvent {\n        let screen: Screen\n        let event: Change<Window>\n    }\n\n    private struct UndeterminedApplication {\n        let application: NSRunningApplication\n        let activationPolicyObservation: NSKeyValueObservation?\n        let isFinishedLaunchingObservation: NSKeyValueObservation?\n\n        func invalidate() {\n            activationPolicyObservation?.invalidate()\n            isFinishedLaunchingObservation?.invalidate()\n        }\n    }\n\n    enum CodingKeys: String, CodingKey {\n        case screens\n    }\n\n    let windowTransitionCoordinator: WindowTransitionCoordinator<WindowManager<Application>>\n    let focusTransitionCoordinator: FocusTransitionCoordinator<WindowManager<Application>>\n\n    private var applications: [pid_t: AnyApplication<Application>] = [:]\n    private var applicationObservations: [pid_t: UndeterminedApplication] = [:]\n    private var screens: Screens\n    private let windows = Windows()\n    private var lastReflowTime = Date()\n    private var lastFocusDate: Date?\n    private var pendingTabDetection: [Window.WindowID: Window] = [:]\n    private var earlyFocusedWindows: Set<Window.WindowID> = []\n    private var eventQueue: [PendingEvent] = []\n\n    private lazy var mouseStateKeeper = MouseStateKeeper(delegate: self)\n    private lazy var applicationEventHandler = ApplicationEventHandler(delegate: self)\n    private let userConfiguration: UserConfiguration\n    private let disposeBag = DisposeBag()\n\n    init(userConfiguration: UserConfiguration) {\n        self.userConfiguration = userConfiguration\n        self.screens = Screens()\n        self.windowTransitionCoordinator = WindowTransitionCoordinator<WindowManager<Application>>()\n        self.focusTransitionCoordinator = FocusTransitionCoordinator<WindowManager<Application>>(userConfiguration: userConfiguration)\n        super.init()\n        initialize()\n    }\n\n    init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        self.screens = try values.decode(Screens.self, forKey: .screens)\n        self.userConfiguration = UserConfiguration.shared\n        self.windowTransitionCoordinator = WindowTransitionCoordinator<WindowManager<Application>>()\n        self.focusTransitionCoordinator = FocusTransitionCoordinator<WindowManager<Application>>(userConfiguration: userConfiguration)\n        super.init()\n        initialize()\n    }\n\n    private func initialize() {\n        windowTransitionCoordinator.target = self\n        focusTransitionCoordinator.target = self\n\n        addWorkspaceNotificationObserver(NSWorkspace.didHideApplicationNotification, selector: #selector(applicationDidHide(_:)))\n        addWorkspaceNotificationObserver(NSWorkspace.didUnhideApplicationNotification, selector: #selector(applicationDidUnhide(_:)))\n        addWorkspaceNotificationObserver(NSWorkspace.activeSpaceDidChangeNotification, selector: #selector(activeSpaceDidChange(_:)))\n\n        NotificationCenter.default.addObserver(\n            self,\n            selector: #selector(screenParametersDidChange(_:)),\n            name: NSApplication.didChangeScreenParametersNotification,\n            object: nil\n        )\n\n        installApplicationMonitor()\n\n        reevaluateWindows()\n        screens.updateScreens(windowManager: self)\n    }\n\n    deinit {\n        NSWorkspace.shared.notificationCenter.removeObserver(self)\n        NotificationCenter.default.removeObserver(self)\n    }\n\n    func reset() {\n        screens = Screens()\n        reevaluateWindows()\n        screens.updateScreens(windowManager: self)\n    }\n\n    private func addWorkspaceNotificationObserver(_ name: NSNotification.Name, selector: Selector) {\n        let workspaceNotificationCenter = NSWorkspace.shared.notificationCenter\n        workspaceNotificationCenter.addObserver(self, selector: selector, name: name, object: nil)\n    }\n\n    @objc func applicationActivated(_ sender: AnyObject) {\n        guard let focusedWindow = Window.currentlyFocused(), let screen = focusedWindow.screen() else {\n            return\n        }\n        markScreenForReflow(screen)\n//        doMouseFollowsFocus(focusedWindow: focusedWindow)\n    }\n\n    @objc func applicationDidLaunch(_ notification: Notification) {\n        guard let launchedApplication = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else {\n            return\n        }\n        add(runningApplication: launchedApplication)\n    }\n\n    @objc func applicationDidTerminate(_ notification: Notification) {\n        guard let terminatedApplication = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else {\n            return\n        }\n\n        guard let application = applicationWithPID(terminatedApplication.processIdentifier) else {\n            return\n        }\n\n        remove(application: application)\n    }\n\n    @objc func applicationDidHide(_ notification: Notification) {\n        guard let hiddenApplication = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else {\n            return\n        }\n\n        guard let application = applicationWithPID(hiddenApplication.processIdentifier) else {\n            return\n        }\n\n        deactivate(application: application)\n    }\n\n    @objc func applicationDidUnhide(_ notification: Notification) {\n        guard let unhiddenApplication = notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication else {\n            return\n        }\n\n        guard let application = applicationWithPID(unhiddenApplication.processIdentifier) else {\n            return\n        }\n\n        application.dropWindowsCache()\n        for window in application.windows() {\n            add(window: window)\n        }\n        activate(application: application)\n    }\n\n    @objc func activeSpaceDidChange(_ notification: Notification) {\n        // Update spaces across screens so that events get distributed to the correct layouts\n        screens.updateSpaces()\n\n        for pendingEvent in eventQueue {\n            distributeEventToScreen(pendingEvent.screen, change: pendingEvent.event)\n        }\n        eventQueue.removeAll()\n\n        pendingTabDetection.removeAll()\n        earlyFocusedWindows.removeAll()\n\n        for runningApplication in NSWorkspace.shared.runningApplications {\n            let pid = runningApplication.processIdentifier\n            guard let application = applicationWithPID(pid) else {\n                continue\n            }\n\n            application.dropWindowsCache()\n\n            for window in application.windows() {\n                add(window: window)\n            }\n        }\n\n        windows.regenerateActiveIDCache()\n        markAllScreensForReflow()\n    }\n\n    @objc func screenParametersDidChange(_ notification: Notification) {\n        screens.updateScreens(windowManager: self)\n    }\n}\n\nextension WindowManager: ApplicationEventHandlerDelegate {\n    private func installApplicationMonitor() {\n        let target = GetApplicationEventTarget()\n        let launchedEventSpec = EventTypeSpec(eventClass: OSType(kEventClassApplication), eventKind: OSType(kEventAppLaunched))\n        let terminatedEventSpec = EventTypeSpec(eventClass: OSType(kEventClassApplication), eventKind: OSType(kEventAppTerminated))\n        var eventSpecs = [launchedEventSpec, terminatedEventSpec]\n        let eventHandler = UnsafeMutableRawPointer(Unmanaged.passUnretained(applicationEventHandler).toOpaque())\n        let error = InstallEventHandler(target, applicationEventHandlerUPP, 2, &eventSpecs, eventHandler, nil)\n\n        if error != noErr {\n            log.error(\"error installing app launch monitor: \\(error)\")\n        }\n    }\n\n    func add(applicationWithPID pid: pid_t) {\n        guard let runningApplication = NSRunningApplication(processIdentifier: pid) else {\n            log.warning(\"process launched with no application: \\(pid)\")\n            return\n        }\n\n        add(runningApplication: runningApplication)\n    }\n\n    func remove(applicationWithPID pid: pid_t) {\n        guard let application = applicationWithPID(pid) else {\n            log.warning(\"process terminated with no application: \\(pid)\")\n            return\n        }\n\n        remove(application: application)\n    }\n}\n\nextension WindowManager {\n    func preferencesDidClose() {\n        DispatchQueue.main.async {\n            self.focusTransitionCoordinator.focusScreen(at: 0)\n        }\n    }\n\n    func focusedScreenManager() -> ScreenManager<WindowManager<Application>>? {\n        return screens.focusedScreenManager()\n    }\n\n    fileprivate func applicationWithPID(_ pid: pid_t) -> AnyApplication<Application>? {\n        return applications[pid]\n    }\n\n    fileprivate func add(application: AnyApplication<Application>) {\n        guard applications[application.pid()] == nil else {\n            for window in application.windows() {\n                add(window: window)\n            }\n            return\n        }\n\n        ApplicationObservation(application: application, delegate: self)\n            .addObservers()\n            .subscribe(\n                onCompleted: { [weak self] in\n                    self?.applications[application.pid()] = application\n\n                    for window in application.windows() {\n                        self?.add(window: window)\n                    }\n                }\n            )\n            .disposed(by: disposeBag)\n    }\n\n    fileprivate func remove(application: AnyApplication<Application>) {\n        for window in application.windows() {\n            remove(window: window)\n        }\n        applications.removeValue(forKey: application.pid())\n    }\n\n    fileprivate func activate(application: AnyApplication<Application>) {\n        windows.activateApplication(withPID: application.pid())\n        windows.regenerateActiveIDCache()\n        markAllScreensForReflow()\n    }\n\n    fileprivate func deactivate(application: AnyApplication<Application>) {\n        windows.deactivateApplication(withPID: application.pid())\n        markAllScreensForReflow()\n    }\n\n    fileprivate func remove(window: Window) {\n        log.debug(\"Removing window: \\(window)\")\n        pendingTabDetection.removeValue(forKey: window.id())\n        earlyFocusedWindows.remove(window.id())\n        distributeEventToAllScreens(.remove(window: window))\n        markAllScreensForReflow()\n        windows.regenerateActiveIDCache()\n        windows.remove(window: window)\n    }\n\n    func toggleFloatForFocusedWindow() {\n        guard let focusedWindow = Window.currentlyFocused(), let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard windows.windows(onScreen: screen).contains(focusedWindow) else {\n            let windowChange: Change<Window> = .add(window: focusedWindow)\n            add(window: focusedWindow)\n            guard windows.window(withID: focusedWindow.id()) != nil else {\n                return\n            }\n            windows.setFloating(false, forWindow: focusedWindow)\n            distributeEventToScreen(screen, change: windowChange)\n            markScreenForReflow(screen)\n            return\n        }\n\n        let windowChange: Change = windows.isWindowFloating(focusedWindow) ? .add(window: focusedWindow) : .remove(window: focusedWindow)\n        windows.setFloating(!windows.isWindowFloating(focusedWindow), forWindow: focusedWindow)\n        distributeEventToScreen(screen, change: windowChange)\n        markScreenForReflow(screen)\n    }\n\n    func distributeEventToScreen(_ screen: Screen, change: Change<Window>) {\n        screens.distributeEventToScreen(screen, change: change)\n    }\n\n    func distributeEventToAllScreens(_ change: Change<Window>) {\n        screens.distributeEventToAllScreens(change: change)\n    }\n\n    func markScreenForReflow(_ screen: Screen) {\n        screens.markScreenForReflow(screen)\n    }\n\n    func markAllScreensForReflow() {\n        screens.markAllScreensForReflow()\n    }\n\n    func displayCurrentLayout() {\n        for screenManager in screens.screenManagers {\n            screenManager.displayLayoutHUD()\n        }\n    }\n\n    func displayWindowCountHUD() {\n        guard userConfiguration.enablesWindowCountHUD() else {\n            return\n        }\n\n        for screenManager in screens.screenManagers {\n            let currentCount = userConfiguration.windowMaxCount() ?? 0\n            let countText = currentCount == 0 ? \"Unlimited\" : \"\\(currentCount)\"\n            let title = \"Window Max Count: \\(countText)\"\n            screenManager.displayCustomHUD(title: title)\n        }\n    }\n\n    func add(runningApplication: NSRunningApplication) {\n        switch runningApplication.isManageable {\n        case .manageable:\n            let application = AnyApplication(Application(runningApplication: runningApplication))\n            add(application: application)\n        case .undetermined:\n            monitorUndeterminedApplication(runningApplication)\n        case .unmanageable:\n            break\n        }\n    }\n\n    func monitorUndeterminedApplication(_ runningApplication: NSRunningApplication) {\n        let pid = runningApplication.processIdentifier\n\n        if let previousApplication = applicationObservations[pid] {\n            previousApplication.invalidate()\n            applicationObservations.removeValue(forKey: pid)\n        }\n\n        let activationPolicyObservation = runningApplication.observe(\\.activationPolicy) { [weak self] runningApplication, change in\n            guard case .setting = change.kind else {\n                return\n            }\n\n            if runningApplication.activationPolicy == .regular {\n                self?.applicationObservations[runningApplication.processIdentifier]?.invalidate()\n                self?.applicationObservations.removeValue(forKey: runningApplication.processIdentifier)\n                self?.add(runningApplication: runningApplication)\n            }\n        }\n\n        let isFinishedLaunchingObservation = runningApplication.observe(\\.isFinishedLaunching) { [weak self] runningApplication, change in\n            guard case .setting = change.kind else {\n                return\n            }\n\n            if runningApplication.isFinishedLaunching {\n                self?.applicationObservations[runningApplication.processIdentifier]?.invalidate()\n                self?.applicationObservations.removeValue(forKey: runningApplication.processIdentifier)\n                self?.add(runningApplication: runningApplication)\n            }\n        }\n\n        applicationObservations[pid] = UndeterminedApplication(\n            application: runningApplication,\n            activationPolicyObservation: activationPolicyObservation,\n            isFinishedLaunchingObservation: isFinishedLaunchingObservation\n        )\n    }\n\n    func reevaluateWindows() {\n        for runningApplication in NSWorkspace.shared.runningApplications {\n            add(runningApplication: runningApplication)\n        }\n        markAllScreensForReflow()\n    }\n\n    private func add(window: Window, afterWindow otherWindow: Window? = nil) {\n        log.debug(\"Adding window: \\(window)\")\n        guard window.shouldBeManaged() else {\n            log.debug(\"Window should not be managed: \\(window)\")\n            return\n        }\n\n        guard let application = applicationWithPID(window.pid()) else {\n            log.error(\"Tried to add a window without an application: \\(window)\")\n            return\n        }\n\n        defer {\n            windows.regenerateActiveIDCache()\n        }\n\n        guard !windows.isWindowTracked(window) else {\n            log.debug(\"Window was already tracked: \\(window)\")\n            return\n        }\n\n        ApplicationObservation(application: application, delegate: self)\n            .addObserversForWindow(window)\n            .map { try self.determineFloatForWindow(window, application: application, force: false) }\n            .retry { error in\n                error.enumerated().flatMap { count, error -> Observable<Int> in\n                    guard error is TrackingError, count < 6 else {\n                        return .error(error)\n                    }\n\n                    log.debug(\"error in determining float for window: \\(window) - \\(error)\")\n                    return .timer(.milliseconds((count ^ 2 * 100)), scheduler: MainScheduler.instance)\n                }\n            }\n            .catch { error in\n                guard error is TrackingError else {\n                    throw error\n                }\n                log.debug(\"forcing float for window: \\(window)\")\n                try self.determineFloatForWindow(window, application: application, force: true)\n                return .just(())\n            }\n            .map { try self.track(window: window, application: application, afterWindow: otherWindow) }\n            .retry { error in\n                error.enumerated().flatMap { count, error -> Observable<Int> in\n                    guard error is TrackingError, count < 6 else {\n                        return .error(error)\n                    }\n\n                    log.debug(\"encountered an error trying to track window: \\(error)\")\n                    return .timer(.milliseconds((count ^ 2 * 100)), scheduler: MainScheduler.instance)\n                }\n            }\n            .subscribe()\n            .disposed(by: disposeBag)\n    }\n\n    private func determineFloatForWindow(_ window: Window, application: AnyApplication<Application>, force: Bool) throws {\n        switch application.defaultFloatForWindow(window) {\n        case .unreliable where !force:\n            throw TrackingError.unreliableFloating\n        case .reliable(.floating), .unreliable(.floating):\n            windows.setFloating(true, forWindow: window)\n        case .reliable(.notFloating), .unreliable(.notFloating):\n            windows.setFloating(false, forWindow: window)\n        }\n    }\n\n    private func track(window: Window, application: AnyApplication<Application>, afterWindow otherWindow: Window? = nil) throws {\n        guard !windows.isWindowTracked(window) else {\n            log.warning(\"Trying to track a window that is already tracked: \\(window)\")\n            throw TrackingError.alreadyTracked\n        }\n\n        guard let screen = window.screen() else {\n            throw TrackingError.unknownScreen\n        }\n\n        guard CGWindowsInfo.windowSpace(window) != nil else {\n            throw TrackingError.unknownSpace\n        }\n\n        if let otherWindow = otherWindow {\n            _ = windows.replace(window: window, withWindow: otherWindow)\n            distributeEventToScreen(screen, change: .tabChange(window: window, previousWindow: otherWindow))\n        } else {\n            windows.add(window: window, atFront: userConfiguration.sendNewWindowsToMainPane())\n\n            // Only send .add to layouts if the window is on the currently active space.\n            // Windows tracked during a space change for a different space should not\n            // generate .add events — doing so gives layouts stale data for windows\n            // that aren't visible on the current space.\n            let windowSpace = CGWindowsInfo.windowSpace(window)\n            let currentSpaceID = CGSpacesInfo<Window>.currentSpaceForScreen(screen)?.id\n            let isOnCurrentSpace: Bool\n            if let currentSpaceID, let windowSpace {\n                isOnCurrentSpace = currentSpaceID == windowSpace\n            } else {\n                isOnCurrentSpace = true\n            }\n\n            if isOnCurrentSpace {\n                let windowChange: Change = windows.isWindowFloating(window) ? .unknown : .add(window: window)\n                distributeEventToScreen(screen, change: windowChange)\n            }\n        }\n\n        markScreenForReflow(screen)\n    }\n\n    /**\n     This function is a best effort to detect changes between native macOS tabs.\n     \n     - Description:\n        Each \"tab\" is an independent window, but the underlying system relates them in some way that we do not have access to. The heuristic is to find a window from the same application that has recently left the screen, and swap them.\n     \n        This performs pretty well in steady state, but can be a bit wonky when finding the existing tabs depending on how quick the transitions are.\n\n     - Parameters:\n        - window: the window that might be a tab change.\n     */\n    func swapInTab(window: Window) {\n        guard let screen = window.screen() else {\n            return\n        }\n\n        // We do this to avoid triggering tab swapping when just switching focus between apps.\n        // If the window's app is not running by this point then it's not a tab switch.\n        guard let runningApp = NSRunningApplication(processIdentifier: window.pid()), runningApp.isActive else {\n            return\n        }\n\n        // We take the windows that are being tracked so we can properly detect when a tab switch is a new tab.\n        // It is important here to compute isActive and isOnScreen as soon as possible for improved accuracy.\n        let applicationWindows = windows.windows(forApplicationWithPID: window.pid())\n            .map { ($0, windows.isWindowActive($0), $0.isOnScreen()) }\n\n        var string = \"\\n\\tNew Window: \\(window)\"\n        applicationWindows.forEach { string += \"\\n\\tExisting window: \\($0)\" }\n        log.debug(string)\n\n        for (existingWindow, isActive, isOnScreen) in applicationWindows {\n            guard existingWindow != window else {\n                log.debug(\"Windows are the same:\\n\\tNew: \\(window)\\n\\t\\(existingWindow)\")\n                continue\n            }\n\n            // The window needs to have been active _at some point_, but must not be currently on screen.\n            let didLeaveScreen = (isActive || windows.isWindowActive(existingWindow)) && !existingWindow.isOnScreen()\n            let isInvalid = existingWindow.cgID() == kCGNullWindowID\n\n            log.debug(\"\"\"\n            Considering window: \\(existingWindow)\n            isActive: \\(isActive), isOnScreen: \\(isOnScreen), isInvalid: \\(isInvalid), managed: \\(existingWindow.shouldBeManaged())\n            Recomputed isActive: \\(windows.isWindowActive(existingWindow)), isOnScreen: \\(existingWindow.isOnScreen())\n            \"\"\")\n\n            // The window needs to have either left the screen and therefore is being replaced\n            // or be invalid and therefore being removed and can be replaced.\n            guard didLeaveScreen || isInvalid else {\n                log.debug(\"Window candidate discarded: \\(existingWindow)\")\n                continue\n            }\n\n            // We have to make sure that we haven't had a focus change too recently as that could mean\n            // the window is already active, but just became focused by swapping window focus.\n            // The time is in seconds, and too long a time ends up with quick switches triggering tabs to incorrectly\n            // swap.\n            let changeInterval = lastFocusDate.flatMap { abs($0.timeIntervalSinceNow) }\n            if let changeInterval = changeInterval, abs(changeInterval) < 0.1 && !isInvalid {\n                log.debug(\"\"\"\n                Window candidate discarded: \\(existingWindow)\n                lastFocusChange: \\(lastFocusDate?.description ?? \"nil\") now: \\(changeInterval) isInvalid: \\(isInvalid)\n                \"\"\")\n                continue\n            }\n\n            log.debug(\"Selected existing window: \\(existingWindow)\")\n\n            guard windows.isWindowTracked(window) else {\n                // If the window isn't tracked we add it in relation to the existing one.\n                pendingTabDetection.removeValue(forKey: window.id())\n                earlyFocusedWindows.remove(window.id())\n                add(window: window, afterWindow: existingWindow)\n                return\n            }\n\n            // If we get here, we are working with a window that has been previously added.\n            // Instead of going through the whole add process, we can just swap the windows in order.\n            pendingTabDetection.removeValue(forKey: window.id())\n            earlyFocusedWindows.remove(window.id())\n            windows.replace(window: existingWindow, withWindow: window)\n            windows.regenerateActiveIDCache()\n\n            // Note that the existing window moving out of screen will be tracked as a remove,\n            // but the \"adding\" happens above, so we need to distribute the relevant change.\n            distributeEventToScreen(screen, change: .tabChange(window: window, previousWindow: existingWindow))\n            markScreenForReflow(screen)\n\n            return\n        }\n\n        windows.regenerateActiveIDCache()\n        if earlyFocusedWindows.remove(window.id()) != nil {\n            // Focus notification already fired before we got here — the visual\n            // transition is settled so call completeTabDetection directly.\n            completeTabDetection(for: window, on: screen)\n        } else {\n            pendingTabDetection[window.id()] = window\n        }\n    }\n\n    private func completeTabDetection(for window: Window, on screen: Screen) {\n        windows.regenerateActiveIDCache()\n\n        let applicationWindows = windows.windows(forApplicationWithPID: window.pid())\n\n        for existingWindow in applicationWindows {\n            guard existingWindow != window else { continue }\n\n            let didLeaveScreen = windows.isWindowActive(existingWindow) && !existingWindow.isOnScreen()\n            let isInvalid = existingWindow.cgID() == kCGNullWindowID\n\n            guard didLeaveScreen || isInvalid else { continue }\n\n            log.debug(\"completeTabDetection: selected candidate \\(existingWindow) for \\(window)\")\n\n            guard windows.isWindowTracked(window) else {\n                pendingTabDetection.removeValue(forKey: window.id())\n                earlyFocusedWindows.remove(window.id())\n                add(window: window, afterWindow: existingWindow)\n                return\n            }\n\n            pendingTabDetection.removeValue(forKey: window.id())\n            earlyFocusedWindows.remove(window.id())\n            windows.replace(window: existingWindow, withWindow: window)\n            windows.regenerateActiveIDCache()\n            distributeEventToScreen(screen, change: .tabChange(window: window, previousWindow: existingWindow))\n            markScreenForReflow(screen)\n            return\n        }\n\n        log.debug(\"completeTabDetection: no candidate found\")\n        add(window: window)\n    }\n\n    func onReflowInitiation() {\n        mouseStateKeeper.handleReflowEvent()\n    }\n\n    func onReflowCompletion() {\n//        if let focusedWindow = Window.currentlyFocused() {\n//            doMouseFollowsFocus(focusedWindow: focusedWindow)\n//        }\n\n        // This handler will be executed by the Operation, in a queue.  Although async\n        // (and although the docs say that it executes in a separate thread), I consider\n        // this to be thread safe, at least safe enough, because we always want the\n        // latest time that a reflow took place.\n        mouseStateKeeper.handleReflowEvent()\n        lastReflowTime = Date()\n    }\n\n    func doMouseFollowsFocus(focusedWindow: Window) {\n        guard UserConfiguration.shared.mouseFollowsFocus() else {\n            return\n        }\n\n        guard NSEvent.pressedMouseButtons == 0 else {\n            // If a mouse button is pressed, then the user is probably dragging something between windows. Do not move the mouse.\n            return\n        }\n\n        // See the description of mouseMoveClickSpeedTolerance for details.\n        if let interval = mouseStateKeeper.lastClick?.timeIntervalSinceNow, abs(interval) < mouseMoveClickSpeedTolerance {\n            return\n        }\n\n        if focusTransitionCoordinator.recentlyTriggeredFocusFollowsMouse() {\n            // If we have recently triggered focus-follows-mouse, then disable mouse-follows-focus. Otherwise, the moment\n            // focus-follows-mouse is triggered, the mouse will jump to the center of the focused window.\n            return\n        }\n\n        let windowFrame = focusedWindow.frame()\n        let mouseCursorPoint = NSPoint(x: windowFrame.midX, y: windowFrame.midY)\n        if let mouseMoveEvent = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: mouseCursorPoint, mouseButton: .left) {\n            mouseMoveEvent.flags = CGEventFlags(rawValue: 0)\n            mouseMoveEvent.post(tap: CGEventTapLocation.cghidEventTap)\n        }\n    }\n}\n\nextension WindowManager: MouseStateKeeperDelegate {\n    func recommendMainPaneRatio(_ ratio: CGFloat) {\n        guard let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager() else { return }\n\n        screenManager.updateCurrentLayout { layout in\n            if let panedLayout = layout as? PanedLayout {\n                panedLayout.recommendMainPaneRatio(ratio)\n            }\n        }\n    }\n\n    func swapDraggedWindowWithDropzone(_ draggedWindow: Window) {\n        guard let screen = draggedWindow.screen() else { return }\n\n        let windows: [Window] = self.windows.windows(onScreen: screen)\n\n        // need to flip mouse coordinate system to fit Amethyst https://stackoverflow.com/a/45289010/2063546\n        let flippedPointerLocation = NSPointToCGPoint(NSEvent.mouseLocation)\n        let unflippedY = Screen.globalHeight() - flippedPointerLocation.y + screen.frameIncludingDockAndMenu().origin.y\n        let pointerLocation = NSPointToCGPoint(NSPoint(x: flippedPointerLocation.x, y: unflippedY))\n\n        if let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager(), let layout = screenManager.currentLayout {\n            let windowSet = self.windows.windowSet(forWindowsOnScreen: screen)\n            if let layoutWindow = layout.windowAtPoint(pointerLocation, of: windowSet, on: screen), let framedWindow = self.windows.window(withID: layoutWindow.id) {\n                executeTransition(.switchWindows(draggedWindow, framedWindow))\n                return\n            }\n        }\n\n        // Ignore if there is no window at that point\n        guard let secondWindow = WindowsInformation.alternateWindowForScreenAtPoint(pointerLocation, withWindows: windows, butNot: draggedWindow) else {\n            return\n        }\n        executeTransition(.switchWindows(draggedWindow, secondWindow))\n    }\n}\n\n// MARK: ApplicationObservationDelegate\nextension WindowManager: ApplicationObservationDelegate {\n    func application(_ application: AnyApplication<Application>, didAddWindow window: Window) {\n        add(window: window)\n    }\n\n    func application(_ application: AnyApplication<Application>, didRemoveWindow window: Window) {\n        remove(window: window)\n    }\n\n    func application(_ application: AnyApplication<Application>, didFocusWindow window: Window) {\n        guard let screen = window.screen() else {\n            return\n        }\n\n        lastFocusDate = Date()\n\n        if pendingTabDetection.removeValue(forKey: window.id()) != nil {\n            completeTabDetection(for: window, on: screen)\n        } else if windows.isWindowTracked(window) {\n            distributeEventToScreen(screen, change: .focusChanged(window: window))\n            markScreenForReflow(screen)\n        } else {\n            // Focus notification arrived before the creation notification.\n            // Record this so swapInTab can call completeTabDetection immediately\n            // rather than deferring to a focus event that has already passed.\n            log.debug(\"Focused untracked window before creation notification - recording early focus: \\(window)\")\n            earlyFocusedWindows.insert(window.id())\n        }\n\n//        doMouseFollowsFocus(focusedWindow: window)\n    }\n\n    func application(_ application: AnyApplication<Application>, didFindPotentiallyNewWindow window: Window) {\n        guard !windows.isWindowTracked(window) else {\n            return\n        }\n\n        swapInTab(window: window)\n    }\n\n    func application(_ application: AnyApplication<Application>, didMoveWindow window: Window) {\n        guard userConfiguration.mouseSwapsWindows() else {\n            return\n        }\n\n        guard let screen = window.screen(), activeWindows(on: screen).contains(window) else {\n            return\n        }\n\n        switch mouseStateKeeper.state {\n        case .dragging:\n            // be aware of last reflow time, again to prevent race condition\n            let reflowEndInterval = Date().timeIntervalSince(lastReflowTime)\n            guard reflowEndInterval > mouseStateKeeper.dragRaceThresholdSeconds else { break }\n\n            // record window and wait for mouse up\n            mouseStateKeeper.state = .moving(window: window)\n        case let .doneDragging(lmbUpMoment):\n            mouseStateKeeper.state = .pointing // flip state first to prevent race condition\n\n            // if mouse button recently came up, assume window move is related\n            let dragEndInterval = Date().timeIntervalSince(lmbUpMoment)\n            guard dragEndInterval < mouseStateKeeper.dragRaceThresholdSeconds else { break }\n\n            mouseStateKeeper.swapDraggedWindowWithDropzone(window)\n        default:\n            break\n        }\n    }\n\n    func application(_ application: AnyApplication<Application>, didResizeWindow window: Window) {\n        guard userConfiguration.mouseResizesWindows() else {\n            return\n        }\n\n        guard let screen = window.screen(), activeWindows(on: screen).contains(window) else {\n            return\n        }\n\n        guard\n            let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager(),\n            let layout = screenManager.currentLayout,\n            layout is PanedLayout\n        else {\n            return\n        }\n\n        guard let oldFrame = layout.assignedFrame(window, of: windows.windowSet(forActiveWindowsOnScreen: screen), on: screen) else {\n            return\n        }\n\n        let ratio = oldFrame.impliedMainPaneRatio(windowFrame: window.frame())\n\n        switch mouseStateKeeper.state {\n        case .dragging, .resizing:\n            // record window and wait for mouse up\n            mouseStateKeeper.state = .resizing(screen: screen, ratio: ratio)\n        case let .doneDragging(lmbUpMoment):\n            // if mouse button recently came up, assume window resize is related\n            let dragEndInterval = Date().timeIntervalSince(lmbUpMoment)\n            if dragEndInterval < mouseStateKeeper.dragRaceThresholdSeconds {\n                mouseStateKeeper.state = .pointing // flip state first to prevent race condition\n\n                if let screenManager: ScreenManager<WindowManager<Application>> = focusedScreenManager() {\n                    screenManager.updateCurrentLayout { layout in\n                        if let panedLayout = layout as? PanedLayout {\n                            panedLayout.recommendMainPaneRatio(ratio)\n                        }\n                    }\n                }\n            }\n        default:\n            break\n        }\n    }\n\n    func applicationDidActivate(_ application: AnyApplication<Application>) {\n        NSObject.cancelPreviousPerformRequests(\n            withTarget: self,\n            selector: #selector(applicationActivated(_:)),\n            object: nil\n        )\n        perform(#selector(applicationActivated(_:)), with: nil, afterDelay: 0.2)\n    }\n}\n\n// MARK: Transition Coordination\nextension WindowManager {\n    func screen(at index: Int) -> Screen? {\n        return screenManager(at: index)?.screen\n    }\n\n    func screenManager(at screenIndex: Int) -> ScreenManager<WindowManager<Application>>? {\n        guard screenIndex > -1 && screenIndex < screens.screenManagers.count else {\n            return nil\n        }\n\n        return screens.screenManagers[screenIndex]\n    }\n\n    func screenManager(for screen: Screen) -> ScreenManager<WindowManager<Application>>? {\n        return screens.screenManagers.first { $0.screen?.screenID() == screen.screenID() }\n    }\n\n    func screenManagerIndex(for screen: Screen) -> Int? {\n        return screens.screenManagers.firstIndex { $0.screen?.screenID() == screen.screenID() }\n    }\n}\n\n// MARK: Window Transition\nextension WindowManager: WindowTransitionTarget {\n    func executeTransition(_ transition: WindowTransition<Window>) {\n        switch transition {\n        case let .switchWindows(window, otherWindow):\n            guard windows.swap(window: window, withWindow: otherWindow) else {\n                return\n            }\n\n            distributeEventToAllScreens(.windowSwap(window: window, otherWindow: otherWindow))\n            markAllScreensForReflow()\n        case let .moveWindowToScreen(window, screen):\n            let currentScreen = window.screen()\n            window.moveScaled(to: screen)\n            if currentScreen != nil {\n                distributeEventToScreen(screen, change: .remove(window: window))\n                markScreenForReflow(screen)\n            }\n            distributeEventToScreen(screen, change: .add(window: window))\n            window.focus()\n        case let .moveWindowToSpaceAtIndex(window, spaceIndex, sourceSpaceIndex):\n            guard\n                let screen = window.screen(),\n                let spaces = CGSpacesInfo<Window>.spacesForAllScreens(includeOnlyUserSpaces: true),\n                spaceIndex < spaces.count\n            else {\n                return\n            }\n\n            let targetSpace = spaces[spaceIndex]\n            guard let targetScreen = CGSpacesInfo<Window>.screenForSpace(space: targetSpace) else {\n                return\n            }\n            distributeEventToScreen(screen, change: .remove(window: window))\n            eventQueue.append(PendingEvent(screen: targetScreen, event: .add(window: window)))\n            window.move(toSpaceAtIndex: UInt(spaceIndex + 1))\n            if targetScreen.screenID() != screen.screenID() {\n                // necessary to set frame here as window is expected to be at origin relative to targe screen when moved, can be improved.\n                window.moveScaled(to: targetScreen)\n            }\n\n            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {\n                if !UserConfiguration.shared.followWindowsThrownBetweenSpaces() {\n                    SISystemWideElement.switch(toSpace: UInt(sourceSpaceIndex + 1))\n                }\n            }\n        case .resetFocus:\n            if let screen = screens.screenManagers.first?.screen {\n                executeTransition(.focusScreen(screen))\n            }\n        }\n    }\n\n    func isWindowFloating(_ window: Window) -> Bool {\n        return windows.isWindowFloating(window)\n    }\n\n    func currentLayout() -> Layout<Application.Window>? {\n        return focusedScreenManager()?.currentLayout\n    }\n\n    func activeWindows(on screen: Screen) -> [Window] {\n        return windows.activeWindows(onScreen: screen).filter { window in\n            return window.shouldBeManaged() && !self.windows.isWindowFloating(window)\n        }\n    }\n\n    func nextScreenIndexClockwise(from screen: Screen) -> Int {\n        guard let screenManagerIndex = self.screenManagerIndex(for: screen) else {\n            return -1\n        }\n\n        return (screenManagerIndex + 1) % (screens.screenManagers.count)\n    }\n\n    func nextScreenIndexCounterClockwise(from screen: Screen) -> Int {\n        guard let screenManagerIndex = self.screenManagerIndex(for: screen) else {\n            return -1\n        }\n\n        return (screenManagerIndex == 0 ? screens.screenManagers.count - 1 : screenManagerIndex - 1)\n    }\n\n    func lastMainWindowForCurrentSpace() -> Window? {\n        guard let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(),\n              let lastMainWindow = windows.lastMainWindows[currentFocusedSpace.id]\n        else {\n            return nil\n        }\n        return lastMainWindow\n    }\n}\n\n// MARK: Focus Transition\nextension WindowManager: FocusTransitionTarget {\n    func windows(onScreen screen: Screen) -> [Window] {\n        return windows.activeWindows(onScreen: screen)\n    }\n\n    func executeTransition(_ transition: FocusTransition<Window>) {\n        switch transition {\n        case let .focusWindow(window):\n            window.focus()\n        case let .focusScreen(screen):\n            screen.focusScreen()\n        }\n    }\n\n    func lastFocusedWindow(on screen: Screen) -> Window? {\n        return screens.screenManagers.first { $0.screen?.screenID() == screen.screenID() }?.lastFocusedWindow\n    }\n\n    func nextWindowIDClockwise(on screen: Screen) -> Window.WindowID? {\n        return screenManager(for: screen)?.nextWindowIDClockwise()\n    }\n\n    func nextWindowIDCounterClockwise(on screen: Screen) -> Window.WindowID? {\n        return screenManager(for: screen)?.nextWindowIDCounterClockwise()\n    }\n}\n\nextension WindowManager: ScreenManagerDelegate {\n    func applyWindowLimit(forScreenManager screenManager: ScreenManager<WindowManager<Application>>, minimizingIn range: (Int) -> Range<Int>) {\n        guard let screen = screenManager.screen else {\n            return\n        }\n\n        let windows = screenManager.currentLayout is FloatingLayout\n            ? self.windows(onScreen: screen).filter { $0.shouldBeManaged() }\n            : activeWindows(on: screen)\n        windows[range(windows.count)].forEach {\n            $0.minimize()\n        }\n    }\n\n    func activeWindowSet(forScreenManager screenManager: ScreenManager<WindowManager<Application>>) -> WindowSet<Window> {\n        return windows.windowSet(forActiveWindowsOnScreen: screenManager.screen!)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/WindowTransitionCoordinator.swift",
    "content": "//\n//  WindowTransitionCoordinator.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/24/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport Silica\n\nenum WindowTransition<Window: WindowType> {\n    typealias Screen = Window.Screen\n    case switchWindows(_ window1: Window, _ window2: Window)\n    case moveWindowToScreen(_ window: Window, screen: Screen)\n    case moveWindowToSpaceAtIndex(_ window: Window, spaceIndex: Int, sourceSpaceIndex: Int)\n    case resetFocus\n}\n\nprotocol WindowTransitionTarget: AnyObject {\n    associatedtype Application: ApplicationType\n    typealias Window = Application.Window\n    typealias Screen = Window.Screen\n\n    func executeTransition(_ transition: WindowTransition<Window>)\n\n    func isWindowFloating(_ window: Window) -> Bool\n    func currentLayout() -> Layout<Application.Window>?\n    func screen(at index: Int) -> Screen?\n    func activeWindows(on screen: Screen) -> [Window]\n    func nextScreenIndexClockwise(from screen: Screen) -> Int\n    func nextScreenIndexCounterClockwise(from screen: Screen) -> Int\n    func lastMainWindowForCurrentSpace() -> Window?\n}\n\nclass WindowTransitionCoordinator<Target: WindowTransitionTarget> {\n    typealias Window = Target.Window\n    typealias Screen = Window.Screen\n\n    weak var target: Target?\n\n    init() {}\n\n    func swapFocusedWindowToMain() {\n        guard let focusedWindow = Window.currentlyFocused(), target?.isWindowFloating(focusedWindow) == false, let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.activeWindows(on: screen), let focusedIndex = windows.firstIndex(of: focusedWindow) else {\n            return\n        }\n\n        if windows.count <= 1 {\n            return\n        }\n\n        if focusedIndex == 0 {\n            guard let lastMainWindow = target?.lastMainWindowForCurrentSpace() else {\n                return\n            }\n            target?.executeTransition(.switchWindows(lastMainWindow, focusedWindow))\n            return\n        }\n\n        if focusedIndex != 0 {\n            // Swap focused window with main window if other window is focused\n            target?.executeTransition(.switchWindows(focusedWindow, windows[0]))\n        }\n    }\n\n    func swapFocusedWindowCounterClockwise() {\n        guard let focusedWindow = Window.currentlyFocused(), target?.isWindowFloating(focusedWindow) == false else {\n            target?.executeTransition(.resetFocus)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.activeWindows(on: screen), let focusedWindowIndex = windows.firstIndex(of: focusedWindow) else {\n            return\n        }\n\n        let windowToSwapWith = windows[(focusedWindowIndex == 0 ? windows.count - 1 : focusedWindowIndex - 1)]\n\n        target?.executeTransition(.switchWindows(focusedWindow, windowToSwapWith))\n    }\n\n    func swapFocusedWindowClockwise() {\n        guard let focusedWindow = Window.currentlyFocused(), target?.isWindowFloating(focusedWindow) == false else {\n            target?.executeTransition(.resetFocus)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let windows = target?.activeWindows(on: screen), let focusedWindowIndex = windows.firstIndex(of: focusedWindow) else {\n            return\n        }\n\n        let windowToSwapWith = windows[(focusedWindowIndex + 1) % windows.count]\n\n        target?.executeTransition(.switchWindows(focusedWindow, windowToSwapWith))\n    }\n\n    func throwToScreenAtIndex(_ screenIndex: Int) {\n        guard let screen = target?.screen(at: screenIndex), let focusedWindow = Window.currentlyFocused() else {\n            return\n        }\n\n        // If the window is already on the screen do nothing.\n        guard let focusedScreen = focusedWindow.screen(), focusedScreen.screenID() != screen.screenID() else {\n            return\n        }\n\n        target?.executeTransition(.moveWindowToScreen(focusedWindow, screen: screen))\n    }\n\n    func swapFocusedWindowScreenClockwise() {\n        guard let focusedWindow = Window.currentlyFocused(), target?.isWindowFloating(focusedWindow) == false else {\n            target?.executeTransition(.resetFocus)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let nextScreenIndex = target?.nextScreenIndexClockwise(from: screen), let nextScreen = target?.screen(at: nextScreenIndex) else {\n            return\n        }\n\n        target?.executeTransition(.moveWindowToScreen(focusedWindow, screen: nextScreen))\n    }\n\n    func swapFocusedWindowScreenCounterClockwise() {\n        guard let focusedWindow = Window.currentlyFocused(), target?.isWindowFloating(focusedWindow) == false else {\n            target?.executeTransition(.resetFocus)\n            return\n        }\n\n        guard let screen = focusedWindow.screen() else {\n            return\n        }\n\n        guard let nextScreenIndex = target?.nextScreenIndexCounterClockwise(from: screen), let nextScreen = target?.screen(at: nextScreenIndex) else {\n            return\n        }\n\n        target?.executeTransition(.moveWindowToScreen(focusedWindow, screen: nextScreen))\n    }\n\n    func pushFocusedWindowToSpace(_ space: Int) {\n        guard let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(), let spaces = CGSpacesInfo<Window>.spacesForAllScreens() else {\n            return\n        }\n\n        guard let index = spaces.firstIndex(of: currentFocusedSpace), index < spaces.count else {\n            return\n        }\n\n        pushFocusedWindowToSpace(space, sourceSpace: index)\n    }\n\n    func pushFocusedWindowToSpace(_ space: Int, sourceSpace: Int) {\n        guard let focusedWindow = Window.currentlyFocused(), focusedWindow.screen() != nil else {\n            return\n        }\n\n        target?.executeTransition(.moveWindowToSpaceAtIndex(focusedWindow, spaceIndex: space, sourceSpaceIndex: sourceSpace))\n    }\n\n    func pushFocusedWindowToSpaceLeft() {\n        guard let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(), let spaces = CGSpacesInfo<Window>.spacesForAllScreens() else {\n            return\n        }\n\n        let filteredSpaces = spaces.filter { $0.type == CGSSpaceTypeUser }\n        guard let index = filteredSpaces.firstIndex(of: currentFocusedSpace), index > 0 else {\n            return\n        }\n\n        pushFocusedWindowToSpace(index - 1, sourceSpace: index)\n    }\n\n    func pushFocusedWindowToSpaceRight() {\n        guard let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(), let spaces = CGSpacesInfo<Window>.spacesForAllScreens() else {\n            return\n        }\n\n        let filteredSpaces = spaces.filter { $0.type == CGSSpaceTypeUser }\n        guard let index = filteredSpaces.firstIndex(of: currentFocusedSpace), index + 1 < spaces.count else {\n            return\n        }\n\n        pushFocusedWindowToSpace(index + 1, sourceSpace: index)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Managers/Windows.swift",
    "content": "//\n//  Windows.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/15/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\nextension WindowManager {\n    class Windows {\n        private(set) var windows: [Window] = []\n        private(set) var lastMainWindows: [CGSSpaceID: Window?] = [:]\n        private var activeIDCache: Set<CGWindowID> = Set()\n        private var deactivatedPIDs: Set<pid_t> = Set()\n        private var floatingMap: [Window.WindowID: Bool] = [:]\n\n        // MARK: Window Filters\n\n        func window(withID id: Window.WindowID) -> Window? {\n            return windows.first { $0.id() == id }\n        }\n\n        func windows(forApplicationWithPID applicationPID: pid_t) -> [Window] {\n            return windows.filter { $0.pid() == applicationPID }\n        }\n\n        func windows(onScreen screen: Screen) -> [Window] {\n            return windows.filter { $0.screen() == screen }\n        }\n\n        func activeWindows(onScreen screen: Screen) -> [Window] {\n            guard let screenID = screen.screenID() else {\n                return []\n            }\n\n            guard let currentSpace = CGSpacesInfo<Window>.currentSpaceForScreen(screen) else {\n                log.warning(\"Could not find a space for screen: \\(screenID)\")\n                return []\n            }\n\n            let screenWindows = windows.filter { window in\n                let space = CGWindowsInfo.windowSpace(window)\n\n                guard let windowScreen = window.screen(), currentSpace.id == space else {\n                    return false\n                }\n\n                let isActive = self.isWindowActive(window)\n                let isHidden = self.isWindowHidden(window)\n                let isFloating = self.isWindowFloating(window)\n\n                return windowScreen.screenID() == screen.screenID() && isActive && !isHidden && !isFloating\n            }\n\n            return screenWindows\n        }\n\n        func activeWindowOnCurrentScreen(atIndex: Int) -> Window? {\n            guard let focusedWindow = Window.currentlyFocused(),\n                  let currentScreen = focusedWindow.screen() else {\n                return nil\n            }\n            let activeWindows = activeWindows(onScreen: currentScreen)\n\n            return activeWindows.indices.contains(atIndex) ? activeWindows[atIndex] : nil\n        }\n\n        // MARK: Adding and Removing\n\n        func add(window: Window, atFront shouldInsertAtFront: Bool) {\n            if shouldInsertAtFront {\n                if let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(),\n                   let firstActiveWindow = activeWindowOnCurrentScreen(atIndex: 0) {\n                    lastMainWindows[currentFocusedSpace.id] = firstActiveWindow\n                }\n\n                windows.insert(window, at: 0)\n            } else {\n                windows.append(window)\n            }\n        }\n\n        func add(window: Window, afterWindow otherWindow: Window) -> Bool {\n            guard let otherWindowIndex = windows.firstIndex(of: otherWindow) else {\n                return false\n            }\n\n            windows.insert(window, at: otherWindowIndex)\n\n            return true\n        }\n\n        func remove(window: Window) {\n            for (_, lastMainWindow) in lastMainWindows where lastMainWindow?.id() == window.id() {\n                if let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace() {\n                    let secondWindow = activeWindowOnCurrentScreen(atIndex: 1)\n                    lastMainWindows[currentFocusedSpace.id] = secondWindow\n                }\n            }\n\n            guard let windowIndex = windows.firstIndex(where: { $0.id() == window.id() }) else {\n                return\n            }\n\n            windows.remove(at: windowIndex)\n        }\n\n        @discardableResult func replace(window: Window, withWindow otherWindow: Window) -> Bool {\n            if let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(),\n               let firstActiveWindow = activeWindowOnCurrentScreen(atIndex: 0) {\n                if firstActiveWindow == window || firstActiveWindow == otherWindow {\n                    lastMainWindows[currentFocusedSpace.id] = firstActiveWindow\n                }\n            }\n\n            guard let otherWindowIndex = windows.firstIndex(of: otherWindow) else {\n                windows.append(otherWindow)\n                return false\n            }\n\n            let windowIndex = windows.firstIndex(of: window)\n            windows[otherWindowIndex] = window\n\n            if let windowIndex {\n                windows.remove(at: windowIndex)\n            }\n\n            return true\n        }\n\n        @discardableResult func swap(window: Window, withWindow otherWindow: Window) -> Bool {\n            if let currentFocusedSpace = CGSpacesInfo<Window>.currentFocusedSpace(),\n               let firstActiveWindow = activeWindowOnCurrentScreen(atIndex: 0) {\n                if firstActiveWindow.id() == window.id() || firstActiveWindow.id() == otherWindow.id() {\n                    lastMainWindows[currentFocusedSpace.id] = firstActiveWindow\n                }\n            }\n\n            if windows.firstIndex(of: window) == nil {\n                windows.append(window)\n            }\n\n            guard let windowIndex = windows.firstIndex(of: window), let otherWindowIndex = windows.firstIndex(of: otherWindow) else {\n                return false\n            }\n\n            guard windowIndex != otherWindowIndex else {\n                return false\n            }\n\n            windows[windowIndex] = otherWindow\n            windows[otherWindowIndex] = window\n\n            return true\n        }\n\n        // MARK: Window States\n\n        func isWindowTracked(_ window: Window) -> Bool {\n            return windows.contains(where: { $0.id() == window.id() })\n        }\n\n        func isWindowActive(_ window: Window) -> Bool {\n            return window.isActive() && activeIDCache.contains(window.cgID())\n        }\n\n        func isWindowHidden(_ window: Window) -> Bool {\n            return deactivatedPIDs.contains(window.pid())\n        }\n\n        func isWindowFloating(_ window: Window) -> Bool {\n            return floatingMap[window.id()] ?? false\n        }\n\n        func setFloating(_ floating: Bool, forWindow window: Window) {\n            floatingMap[window.id()] = floating\n        }\n\n        func activateApplication(withPID pid: pid_t) {\n            deactivatedPIDs.remove(pid)\n        }\n\n        func deactivateApplication(withPID pid: pid_t) {\n            deactivatedPIDs.insert(pid)\n        }\n\n        func regenerateActiveIDCache() {\n            let windowDescriptions = CGWindowsInfo<Window>(options: .optionOnScreenOnly, windowID: CGWindowID(0))\n            activeIDCache = windowDescriptions?.activeIDs() ?? Set()\n        }\n\n        // MARK: Window Sets\n\n        func windowSet(forWindowsOnScreen screen: Screen) -> WindowSet<Window> {\n            return windowSet(forWindows: windows(onScreen: screen))\n        }\n\n        func windowSet(forActiveWindowsOnScreen screen: Screen) -> WindowSet<Window> {\n            return windowSet(forWindows: activeWindows(onScreen: screen))\n        }\n\n        func windowSet(forWindows windows: [Window]) -> WindowSet<Window> {\n            let layoutWindows: [LayoutWindow<Window>] = windows.map {\n                LayoutWindow(id: $0.id(), frame: $0.frame(), isFocused: $0.isFocused())\n            }\n\n            return WindowSet<Window>(\n                windows: layoutWindows,\n                isWindowWithIDActive: { [weak self] id -> Bool in\n                    guard let window = self?.window(withID: id) else {\n                        return false\n                    }\n                    return self?.isWindowActive(window) ?? false\n                },\n                isWindowWithIDFloating: { [weak self] windowID -> Bool in\n                    guard let window = self?.window(withID: windowID) else {\n                        return false\n                    }\n                    return self?.isWindowFloating(window) ?? false\n                },\n                windowForID: { [weak self] windowID -> Window? in\n                    return self?.window(withID: windowID)\n                }\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/Application.swift",
    "content": "//\n//  Application.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/12/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\nimport Foundation\nimport Silica\n\n/// Generic protocol for objects acting as applications in the system.\nprotocol ApplicationType: Equatable {\n    /// The type of windows that are used by the application.\n    associatedtype Window: WindowType\n\n    /**\n     Initialize an application based on its corresponding `NSRunningApplication` if it exists.\n     \n     - Parameters:\n         - runningApplication: The running application to find application for.\n     */\n    init(runningApplication: NSRunningApplication)\n\n    /// The optional title of the application\n    func title() -> String?\n\n    /**\n     The windows owned by the application.\n     \n     - Note:\n     This value is cached. Call `dropWindowsCache()` if you believe this may be out of date.\n     */\n    func windows() -> [Window]\n\n    /// The process ID of the application.\n    func pid() -> pid_t\n\n    /**\n     Determines whether a window should float by default.\n     \n     - Parameters:\n         - window: The window to test.\n     \n     - Note:\n     We can receive an unreliable result. It is up to the caller to determine whether or not that result is good enough.\n     */\n    func defaultFloatForWindow(_ window: Window) -> Reliable<DefaultFloat>\n\n    /// Clears the internal cache of application windows.\n    func dropWindowsCache()\n\n    /**\n     Observe an AX notification on the application itself with a given handler.\n     \n     To remove the observation you must call `unobserve(notification:)`.\n     \n     - Parameters:\n         - notification: The notification name to be observing for.\n         - handler: The callback when the notification is triggered.\n     \n     - Returns:\n     `true` if observing the notification succeeded, and `false` otherwise.\n     */\n    func observe(notification: String, handler: @escaping SIAXNotificationHandler) -> AXError\n\n    /**\n     Observe an AX notification on a window of the application with a given handler.\n     \n     To remove the observation you must call `unobserve(notification:window:)`.\n     \n     - Parameters:\n         - notification: The notification name to be observing for.\n         - window: The window being watched for events.\n         - handler: The callback when the notification is triggered.\n     \n     - Returns:\n     `true` if observing the notification succeeded, and `false` otherwise.\n     */\n    func observe(notification: String, window: Window, handler: @escaping SIAXNotificationHandler) -> AXError\n\n    /**\n     Removes an observation for a notification on the application itself.\n     \n     - Parameters:\n         - notification: The notification name to stop observing for.\n     */\n    func unobserve(notification: String)\n\n    /**\n     Removes an observation for a notification on the application itself.\n     \n     - Parameters:\n         - notification: The notification name to stop observing for.\n         - window: The window to stop watching for events.\n     */\n    func unobserve(notification: String, window: Window)\n}\n\n/**\n Type-erased concerete application for managing applications.\n \n This is necessitated by `ApplicationType` having an associated type which prevents it from being used directly as a concrete type.\n */\nclass AnyApplication<Application: ApplicationType>: ApplicationType {\n    /// The window being used is the window being used by the contained application type.\n    typealias Window = Application.Window\n\n    /// The application being contained.\n    private let internalApplication: Application\n\n    /// Comparison for `Equatable` conformance.\n    static func == (lhs: AnyApplication<Application>, rhs: AnyApplication<Application>) -> Bool {\n        return lhs.internalApplication == rhs.internalApplication\n    }\n\n    /**\n     Initializes an application based on another application.\n     \n     - Parameters:\n         - application: The application to be contained.\n     */\n    required init(_ application: Application) {\n        self.internalApplication = application\n    }\n\n    /// It is an error to call this initializer.\n    required init(runningApplication: NSRunningApplication) {\n        fatalError()\n    }\n\n    func title() -> String? {\n        return internalApplication.title()\n    }\n\n    func windows() -> [Window] {\n        return internalApplication.windows()\n    }\n\n    func pid() -> pid_t {\n        return internalApplication.pid()\n    }\n\n    func defaultFloatForWindow(_ window: Window) -> Reliable<DefaultFloat> {\n        return internalApplication.defaultFloatForWindow(window)\n    }\n\n    func dropWindowsCache() {\n        internalApplication.dropWindowsCache()\n    }\n\n    func observe(notification: String, handler: @escaping SIAXNotificationHandler) -> AXError {\n        return internalApplication.observe(notification: notification, handler: handler)\n    }\n\n    func observe(notification: String, window: Window, handler: @escaping SIAXNotificationHandler) -> AXError {\n        return internalApplication.observe(notification: notification, window: window, handler: handler)\n    }\n\n    func unobserve(notification: String) {\n        internalApplication.unobserve(notification: notification)\n    }\n\n    func unobserve(notification: String, window: Window) {\n        internalApplication.unobserve(notification: notification, window: window)\n    }\n}\n\n/// Conformance of `SIApplication` as an Amethyst application.\nextension SIApplication: ApplicationType {\n    /// `SIApplication` uses `AXWindow` as its window type.\n    typealias Window = AXWindow\n\n    convenience init?(pid: pid_t) {\n        guard let runningApplication = NSRunningApplication(processIdentifier: pid) else {\n            return nil\n        }\n\n        self.init(runningApplication: runningApplication)\n    }\n\n    func windows() -> [Window] {\n        let axWindows: [SIWindow] = self.windows()\n        return axWindows.map { Window(axElement: $0.axElementRef) }\n    }\n\n    func pid() -> pid_t {\n        return processIdentifier()\n    }\n\n    func observe(notification: String, handler: @escaping SIAXNotificationHandler) -> AXError {\n        return observeNotification(notification as CFString, with: self, handler: handler)\n    }\n\n    func observe(notification: String, window: Window, handler: @escaping SIAXNotificationHandler) -> AXError {\n        return observeNotification(notification as CFString, with: window, handler: handler)\n    }\n\n    func unobserve(notification: String) {\n        unobserveNotification(notification as CFString, with: self)\n    }\n\n    func unobserve(notification: String, window: Window) {\n        unobserveNotification(notification as CFString, with: window)\n    }\n\n    func defaultFloatForWindow(_ window: Window) -> Reliable<DefaultFloat> {\n        if window.shouldFloat() {\n            return .reliable(.floating)\n        }\n\n        guard let runningApplication = NSRunningApplication(processIdentifier: pid()) else {\n            return .reliable(.floating)\n        }\n\n        return UserConfiguration.shared.runningApplication(runningApplication, byDefaultFloatsForTitle: window.title())\n    }\n\n    private func observe(notification: String, with accessibilityElement: SIAccessibilityElement, handler: @escaping SIAXNotificationHandler) -> AXError {\n        return observeNotification(notification as CFString, with: accessibilityElement, handler: handler)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/ApplicationEventHandler.swift",
    "content": "//\n//  ApplicationEventHandler.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/28/23.\n//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Carbon\nimport Foundation\n\n// swiftlint:disable identifier_name\n@_silgen_name(\"GetProcessPID\") @discardableResult\nfunc GetProcessPID(_ psn: inout ProcessSerialNumber, _ pid: inout pid_t) -> OSStatus\n// swiftlint:enable identifier_name\n\nprotocol ApplicationEventHandlerDelegate: AnyObject {\n    func add(applicationWithPID: pid_t)\n    func remove(applicationWithPID: pid_t)\n}\n\nfunc applicationEventHandlerUPP(_ call: EventHandlerCallRef?, _ event: EventRef?, _ data: UnsafeMutableRawPointer?) -> OSStatus {\n    guard let data = data, let event = event else {\n        return OSStatus(eventNotHandledErr)\n    }\n\n    let handler = Unmanaged<ApplicationEventHandler>.fromOpaque(data).takeUnretainedValue()\n    return handler.handleEvent(event)\n}\n\nclass ApplicationEventHandler {\n    private enum EventType {\n        case applicationLaunched\n        case applicationTerminated\n    }\n\n    private enum EventError: Error {\n        case failedToGetPSN\n        case failedToGetPID\n        case processNotFound\n    }\n\n    private struct Event {\n        let ref: EventRef\n        let eventType: EventType\n\n        func pid() throws -> pid_t {\n            var psn = ProcessSerialNumber()\n            var error = GetEventParameter(ref, EventParamName(kEventParamProcessID), EventParamName(typeProcessSerialNumber), nil, MemoryLayout<ProcessSerialNumber>.size, nil, &psn)\n            guard error == noErr else {\n                log.error(error)\n                throw EventError.failedToGetPSN\n            }\n\n            var pid = pid_t()\n            error = GetProcessPID(&psn, &pid)\n\n            guard error == noErr else {\n                switch error {\n                case OSStatus(procNotFound):\n                    throw EventError.processNotFound\n                default:\n                    throw EventError.failedToGetPID\n                }\n            }\n\n            return pid\n        }\n    }\n\n    weak var delegate: ApplicationEventHandlerDelegate?\n\n    init(delegate: ApplicationEventHandlerDelegate) {\n        self.delegate = delegate\n    }\n\n    func handleEvent(_ event: EventRef) -> OSStatus {\n        switch GetEventKind(event) {\n        case UInt32(kEventAppLaunched):\n            return processEvent(Event(ref: event, eventType: .applicationLaunched))\n        case UInt32(kEventAppTerminated):\n            return processEvent(Event(ref: event, eventType: .applicationTerminated))\n        default:\n            return OSStatus(eventNotHandledErr)\n        }\n    }\n\n    private func processEvent(_ event: Event) -> OSStatus {\n        do {\n            let pid = try event.pid()\n            switch event.eventType {\n            case .applicationLaunched:\n                delegate?.add(applicationWithPID: pid)\n            case .applicationTerminated:\n                delegate?.remove(applicationWithPID: pid)\n            }\n        } catch {\n            log.error(error)\n            return OSStatus(eventNotHandledErr)\n        }\n\n        return noErr\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/ApplicationObservation.swift",
    "content": "//\n//  ApplicationObservation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\nimport Cocoa\nimport Foundation\nimport RxSwift\n\n/// Delegate for handling application observer events.\nprotocol ApplicationObservationDelegate: AnyObject {\n    associatedtype Application: ApplicationType\n    typealias Window = Application.Window\n\n    /**\n     Called when the application has added a window to being active.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was added.\n     \n     - Note: `window` is not necessarily newly created.\n     */\n    func application(_ application: AnyApplication<Application>, didAddWindow window: Window)\n\n    /**\n     Called when the application has removed a window from being active.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was removed.\n     */\n    func application(_ application: AnyApplication<Application>, didRemoveWindow window: Window)\n\n    /**\n     Called when the application has focused a window.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was focused.\n     */\n    func application(_ application: AnyApplication<Application>, didFocusWindow window: Window)\n\n    /**\n     Called when the application has encountered a window that is potentially new.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was encountered.\n     \n     - Note:\n     This is the event that is called when native tab switching happens.\n     */\n    func application(_ application: AnyApplication<Application>, didFindPotentiallyNewWindow window: Window)\n\n    /**\n     Called when the application has moved a window.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was moved.\n     */\n    func application(_ application: AnyApplication<Application>, didMoveWindow window: Window)\n\n    /**\n     Called when the application has resized a window.\n     \n     - Parameters:\n         - application: The application the event occurred in.\n         - window: The window that was resized.\n     */\n    func application(_ application: AnyApplication<Application>, didResizeWindow window: Window)\n\n    /**\n     Called when the application is activated.\n     \n     - Parameters:\n         - application: The application that was activated.\n     */\n    func applicationDidActivate(_ application: AnyApplication<Application>)\n}\n\n/**\n This struct sets up accessibility API event subscriptions for a given Application.\n \n Handling references to the window manager and mouse state. The observers themselves react to mouse / accessibility state by either changing window positions or updating the mouse state based on new information.\n */\nstruct ApplicationObservation<Delegate: ApplicationObservationDelegate> {\n    typealias Application = Delegate.Application\n    typealias Window = Application.Window\n\n    /// Errors when attempting to add observers to applications\n    enum Error: Swift.Error {\n        /// General failure\n        case failed\n\n        /// Failure in the accessibility observation\n        case observationFailed(error: Int32)\n    }\n\n    /// Notifications that are observed\n    private enum Notification {\n        /// A window is created\n        case created\n\n        /// A window is deminiaturized\n        case windowDeminiaturized\n\n        /// A window is miniaturized\n        case windowMiniaturized\n\n        /// The application has changed its focused window\n        case focusedWindowChanged\n\n        /// The application is activated\n        case applicationActivated\n\n        /// A window is moved\n        case windowMoved\n\n        /// A window is resized\n        case windowResized\n\n        /// The application changed its primary window\n        case mainWindowChanged\n\n        /// The window has likely been destroyed\n        case elementDestroyed(window: Window)\n\n        /// The actual notification name\n        var string: String {\n            switch self {\n            case .created:\n                return kAXCreatedNotification\n            case .windowDeminiaturized:\n                return kAXWindowDeminiaturizedNotification\n            case .windowMiniaturized:\n                return kAXWindowMiniaturizedNotification\n            case .focusedWindowChanged:\n                return kAXFocusedWindowChangedNotification\n            case .applicationActivated:\n                return kAXApplicationActivatedNotification\n            case .windowMoved:\n                return kAXWindowMovedNotification\n            case .windowResized:\n                return kAXWindowResizedNotification\n            case .mainWindowChanged:\n                return kAXMainWindowChangedNotification\n            case .elementDestroyed:\n                return kAXUIElementDestroyedNotification\n            }\n        }\n\n        /// Notifications relevant to the entire application\n        static var applicationNotifications: [Notification] {\n            return [\n                .created,\n                .windowDeminiaturized,\n                .windowMiniaturized,\n                .focusedWindowChanged,\n                .applicationActivated,\n                .windowMoved,\n                .windowResized,\n                .mainWindowChanged\n            ]\n        }\n\n        /// Notifications relevant to a particular window of an application\n        static func windowNotificationsForWindow(_ window: Window) -> [Notification] {\n            return [\n                .elementDestroyed(window: window)\n            ]\n        }\n    }\n\n    /// The application being observed\n    let application: AnyApplication<Application>\n\n    /// The delegate for handling events as they come in\n    private weak var delegate: Delegate?\n\n    /**\n     - Parameters:\n         - application: The application to be observed.\n         - delegate: The delegate to handle events.\n     */\n    init(application: AnyApplication<Application>, delegate: Delegate?) {\n        self.application = application\n        self.delegate = delegate\n    }\n\n    /**\n     - Returns:\n     An observable that attemps to subscribe to events on the application. The observable completes when subscriptions have been put in place, and errors otherwise.\n     */\n    func addObservers() -> Observable<Void> {\n        return _addObservers(notifications: Notification.applicationNotifications).retry { errorTrigger in\n            errorTrigger.enumerated().flatMap { count, error -> Observable<Int> in\n                guard count < 6 else {\n                    return .error(error)\n                }\n\n                return .timer(.milliseconds((count ^ 2 * 100)), scheduler: MainScheduler.instance)\n            }\n        }\n    }\n\n    /**\n     - Returns:\n     An observable that attempts to subscribe to events specifically relevant to a window in an application. The observable completes when subscriptions have been put in place, and errors otherwise.\n     */\n    func addObserversForWindow(_ window: Window) -> Observable<Void> {\n        let notifications = Notification.windowNotificationsForWindow(window)\n        return _addObservers(notifications: notifications).retry { errorTrigger in\n            errorTrigger.enumerated().flatMap { count, error -> Observable<Int> in\n                guard count < 6 else {\n                    return .error(error)\n                }\n\n                return .timer(.milliseconds((count ^ 2 * 100)), scheduler: MainScheduler.instance)\n            }\n        }\n    }\n\n    /**\n     - Returns:\n     An observable that unsubscribes from events that may be tracked for a specific window.\n\n     - Parameters:\n        - window: the window on which notifications may be observed.\n     \n     - Note:\n     This is a no op in the case where no notifications were being observed.\n     */\n    func removeObserversForWindow(_ window: Window) {\n        let notifications = Notification.windowNotificationsForWindow(window)\n        removeObservers(notifications: notifications, for: window)\n    }\n\n    private func _addObservers(notifications: [Notification]) -> Observable<Void> {\n        return Observable.from(notifications)\n            .scan([]) { observed, notification -> [Notification] in\n                let notifications = observed + [notification]\n\n                do {\n                    try self.addObserver(for: notification)\n                } catch {\n                    let applicationTitle = self.application.title() ?? \"<unknown>\"\n                    log.error(\"Failed to add observer \\(notification) on application \\(applicationTitle) (\\(self.application.pid())): \\(error)\")\n                    self.removeObservers(notifications: notifications)\n                    throw error\n                }\n\n                return notifications\n            }\n            .map { _ in }\n    }\n\n    /**\n     Observes a specific notification.\n     \n     - Parameters:\n         - notification: The notification to observe.\n     \n     - Throws:\n     An error when failing to add observer.\n     */\n    private func addObserver(for notification: Notification) throws {\n        let success: AXError\n        switch notification {\n        case .elementDestroyed(let window):\n            success = application.observe(notification: notification.string, window: window) { _ in\n                DispatchQueue.main.async {\n                    self.handle(notification: notification, window: window)\n                }\n            }\n        default:\n            success = application.observe(notification: notification.string) { element in\n                guard let window = Window(element: element) else {\n                    return\n                }\n\n                DispatchQueue.main.async {\n                    self.handle(notification: notification, window: window)\n                }\n            }\n        }\n\n        switch success {\n        case .success:\n            return\n        case .notificationAlreadyRegistered:\n            return\n        default:\n            throw Error.observationFailed(error: success.rawValue)\n        }\n    }\n\n    /**\n     Removes notifications from being observed.\n     \n     - Parameters:\n         - notifications: The notifications to stop observing.\n     */\n    private func removeObservers(notifications: [Notification]) {\n        notifications.forEach { application.unobserve(notification: $0.string) }\n    }\n\n    /**\n     Removes notifications from being observed for a window.\n     \n     - Parameters:\n        - notifications: the notifications to stop observing.\n        - window: the window for which notifications were being observed.\n     */\n    private func removeObservers(notifications: [Notification], for window: Window) {\n        notifications.forEach { application.unobserve(notification: $0.string, window: window) }\n    }\n\n    private func handle(notification: Notification, window: Window) {\n        log.debug(\"\"\"\n        Received notification for window: \\(window)\n            notification: \\(notification)\n            \\(window.title() ?? \"no title\") (\\(window.id()))\n        \"\"\")\n        switch notification {\n        case .created:\n            // Disabling window creations because they end up being more reliably tracked with main window changed\n//            delegate?.application(application, didFindPotentiallyNewWindow: window)\n            break\n        case .windowDeminiaturized:\n            delegate?.application(application, didAddWindow: window)\n        case .windowMiniaturized:\n            delegate?.application(application, didRemoveWindow: window)\n        case .focusedWindowChanged:\n            guard let focusedWindow = Window.currentlyFocused() else {\n                return\n            }\n            delegate?.application(application, didFocusWindow: focusedWindow)\n        case .applicationActivated:\n            delegate?.applicationDidActivate(application)\n        case .windowMoved:\n            delegate?.application(application, didMoveWindow: window)\n        case .windowResized:\n            delegate?.application(application, didResizeWindow: window)\n        case .mainWindowChanged:\n            delegate?.application(application, didFindPotentiallyNewWindow: window)\n        case .elementDestroyed:\n            delegate?.application(application, didRemoveWindow: window)\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/CGInfo.swift",
    "content": "//\n//  CGInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\nimport SwiftyJSON\n\n/// Windows info as taken from the underlying system.\nstruct CGWindowsInfo<Window: WindowType> {\n    /// An array of dictionaries of window information\n    let descriptions: [[String: AnyObject]]\n\n    /**\n     - Parameters:\n     - options: Any options for getting info.\n     - windowID: ID of window to find windows relative to. 0 gets all windows.\n     */\n    init?(options: CGWindowListOption, windowID: CGWindowID) {\n        guard let cfWindowDescriptions = CGWindowListCopyWindowInfo(options, windowID) else {\n            return nil\n        }\n\n        guard let windowDescriptions = cfWindowDescriptions as? [[String: AnyObject]] else {\n            return nil\n        }\n\n        self.descriptions = windowDescriptions\n    }\n\n    /**\n     - Returns:\n     The set of windows that are currently active.\n     */\n    func activeIDs() -> Set<CGWindowID> {\n        var ids: Set<CGWindowID> = Set()\n\n        for windowDescription in descriptions {\n            guard let windowID = windowDescription[kCGWindowNumber as String] as? NSNumber else {\n                continue\n            }\n\n            ids.insert(CGWindowID(windowID.uint64Value))\n        }\n\n        return ids\n    }\n\n    static func windowIDsArray(_ window: Window) -> NSArray {\n        return [NSNumber(value: window.cgID() as UInt32)] as NSArray\n    }\n\n    static func windowSpace(_ window: Window) -> Int? {\n        let windowIDsArray = CGWindowsInfo.windowIDsArray(window)\n\n        guard let cfSpaces = CGSCopySpacesForWindows(CGSMainConnectionID(), kCGSAllSpacesMask, windowIDsArray)?.takeRetainedValue() else {\n            return nil\n        }\n\n        guard let spaces = cfSpaces as NSArray as? [NSNumber] else {\n            return nil\n        }\n\n        guard !spaces.isEmpty else {\n            return nil\n        }\n\n        return spaces.first?.intValue\n    }\n}\n\nstruct CGScreensInfo<Window: WindowType> {\n    typealias Screen = Window.Screen\n\n    let descriptions: [JSON]\n\n    init?() {\n        guard let descriptions = Screen.screenDescriptions() else {\n            return nil\n        }\n\n        self.descriptions = descriptions\n    }\n\n    func space(at index: Int) -> Space {\n        return CGSpacesInfo<Window>.space(fromScreenDescription: descriptions[index])\n    }\n}\n\nstruct CGSpacesInfo<Window: WindowType> {\n    typealias Screen = Window.Screen\n\n    static func spacesForAllScreens(includeOnlyUserSpaces: Bool = false) -> [Space]? {\n        guard let screenDescriptions = Screen.screenDescriptions() else {\n            return nil\n        }\n\n        guard !screenDescriptions.isEmpty else {\n            return nil\n        }\n\n        let spaces = screenDescriptions.map { screenDescription -> [Space] in\n            return allSpaces(fromScreenDescription: screenDescription) ?? []\n        }.reduce([], {acc, spaces in acc + spaces})\n\n        if includeOnlyUserSpaces {\n            return spaces.filter { $0.type == CGSSpaceTypeUser }\n        }\n\n        return spaces\n    }\n\n    static func spacesForScreen(_ screen: Screen, includeOnlyUserSpaces: Bool = false) -> [Space]? {\n        guard let screenDescriptions = Screen.screenDescriptions() else {\n            return nil\n        }\n\n        guard !screenDescriptions.isEmpty else {\n            return nil\n        }\n\n        let screenID = screen.screenID()\n        let spaces: [Space]?\n\n        if Screen.screensHaveSeparateSpaces {\n            spaces = screenDescriptions\n                .first { $0[\"Display Identifier\"].string == screenID }\n                .flatMap { screenDescription -> [Space]? in\n                    return allSpaces(fromScreenDescription: screenDescription)\n                }\n        } else {\n            spaces = allSpaces(fromScreenDescription: screenDescriptions[0])\n        }\n\n        if includeOnlyUserSpaces {\n            return spaces?.filter { $0.type == CGSSpaceTypeUser }\n        }\n\n        return spaces\n    }\n\n    static func spacesForFocusedScreen() -> [Space]? {\n        guard let focusedWindow = Window.currentlyFocused(), let screen = focusedWindow.screen() else {\n            return nil\n        }\n\n        return spacesForScreen(screen)\n    }\n\n    static func currentSpaceForScreen(_ screen: Screen) -> Space? {\n        guard let screenDescriptions = Screen.screenDescriptions(), let screenID = screen.screenID() else {\n            return nil\n        }\n\n        guard screenDescriptions.count > 0 else {\n            return nil\n        }\n\n        if Screen.screensHaveSeparateSpaces {\n            for screenDescription in screenDescriptions {\n                guard screenDescription[\"Display Identifier\"].string == screenID else {\n                    continue\n                }\n\n                return space(fromScreenDescription: screenDescription)\n            }\n        } else {\n            return space(fromScreenDescription: screenDescriptions[0])\n        }\n\n        return nil\n    }\n\n    static func currentFocusedSpace() -> Space? {\n        guard let focusedWindow = Window.currentlyFocused(), let screen = focusedWindow.screen() else {\n            return nil\n        }\n\n        return currentSpaceForScreen(screen)\n    }\n\n    static func screenForSpace(space: Space) -> Screen? {\n        return Screen.availableScreens.first { spacesForScreen($0)?.contains { $0.id == space.id } ?? false }\n    }\n\n    static func space(fromScreenDescription screenDictionary: JSON) -> Space {\n        return space(fromSpaceDescription: screenDictionary[\"Current Space\"])\n    }\n\n    static func space(fromSpaceDescription spaceDictionary: JSON) -> Space {\n        let id: CGSSpaceID = spaceDictionary[\"ManagedSpaceID\"].intValue\n        let type = CGSSpaceType(rawValue: spaceDictionary[\"type\"].uInt32Value)\n        let uuid = spaceDictionary[\"uuid\"].stringValue\n        return Space(id: id, type: type, uuid: uuid)\n    }\n\n    static func allSpaces(fromScreenDescription screenDictionary: JSON) -> [Space]? {\n        return screenDictionary[\"Spaces\"].array?.map {\n            space(fromSpaceDescription: $0)\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/Change.swift",
    "content": "//\n//  Change.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\n\nenum Change<Window: WindowType> {\n    case add(window: Window)\n    case remove(window: Window)\n    case focusChanged(window: Window)\n    case windowSwap(window: Window, otherWindow: Window)\n    case applicationActivate\n    case applicationDeactivate\n    case spaceChange\n    case layoutChange\n    case tabChange(window: Window, previousWindow: Window)\n    case none\n    case unknown\n}\n"
  },
  {
    "path": "Amethyst/Model/MouseState.swift",
    "content": "//\n//  MouseState.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\n/**\n These are the possible actions that the mouse might be taking (that we care about).\n \n We use this enum to convey some information about the window that the mouse might be interacting with.\n */\nenum MouseState<Window: WindowType> {\n    typealias Screen = Window.Screen\n    case pointing\n    case clicking\n    case dragging\n    case moving(window: Window)\n    case resizing(screen: Screen, ratio: CGFloat)\n    case doneDragging(atTime: Date)\n}\n\n/// MouseStateKeeper will need a few things to do its job effectively\nprotocol MouseStateKeeperDelegate: AnyObject {\n    associatedtype Window: WindowType\n    func recommendMainPaneRatio(_ ratio: CGFloat)\n    func swapDraggedWindowWithDropzone(_ draggedWindow: Window)\n}\n\n/**\n Maintains state information about the mouse for the purposes of mouse-based window operations.\n \n MouseStateKeeper exists because we need a single shared mouse state between all applications being observed. This class captures the state and coordinates any Amethyst reflow actions that are required in response to mouse events.\n \n Note that some actions may be initiated here and some actions may be completed here; we don't know whether the mouse event stream or the accessibility event stream will fire first.\n \nThis class by itself can only understand clicking, dragging, and \"pointing\" (no mouse buttons down). The SIApplication observers are able to augment that understanding of state by \"upgrading\" a drag action to a \"window move\" or a \"window resize\" event since those observers will have proper context.\n */\nclass MouseStateKeeper<Delegate: MouseStateKeeperDelegate> {\n    let dragRaceThresholdSeconds = 0.15 // prevent race conditions during drag ops\n    var state: MouseState<Delegate.Window>\n    private(set) weak var delegate: Delegate?\n    private(set) var lastClick: Date?\n    private var monitor: Any?\n\n    init(delegate: Delegate) {\n        self.delegate = delegate\n\n        state = .pointing\n        let mouseEventsToWatch: NSEvent.EventTypeMask = [.leftMouseDown, .leftMouseUp, .leftMouseDragged]\n        monitor = NSEvent.addGlobalMonitorForEvents(matching: mouseEventsToWatch, handler: self.handleMouseEvent)\n    }\n\n    deinit {\n        guard let oldMonitor = monitor else { return }\n        NSEvent.removeMonitor(oldMonitor)\n    }\n\n    // Update our understanding of the current state unless an observer has already\n    // done it for us.  mouseUp events take precedence over anything an observer had\n    // found -- you can't be dragging or resizing with a mouse button up, even if\n    // you're using the \"3 finger drag\" accessibility option, where no physical button\n    // is being pressed.\n    func handleMouseEvent(anEvent: NSEvent) {\n        switch anEvent.type {\n        case .leftMouseDown:\n            self.state = .clicking\n        case .leftMouseDragged:\n            switch self.state {\n            case .moving, .resizing:\n            break // ignore - we have what we need\n            case .pointing, .clicking, .dragging, .doneDragging:\n                self.state = .dragging\n            }\n\n        case .leftMouseUp:\n            switch self.state {\n            case .dragging:\n                // assume window move event will come shortly after\n                self.state = .doneDragging(atTime: Date())\n            case let .moving(draggedWindow):\n                self.state = .pointing // flip state first to prevent race condition\n                self.swapDraggedWindowWithDropzone(draggedWindow)\n            case let .resizing(_, ratio):\n                self.state = .pointing\n                self.resizeFrameToDraggedWindowBorder(ratio)\n            case .doneDragging:\n                self.state = .doneDragging(atTime: Date()) // reset the clock I guess\n            case .clicking:\n                lastClick = Date()\n                self.state = .pointing\n            case .pointing:\n                self.state = .pointing\n            }\n\n        default: ()\n        }\n    }\n\n    // React to a reflow event.  Typically this means that any window we were dragging\n    // is no longer valid and should be de-correlated from the mouse\n    func handleReflowEvent() {\n        switch self.state {\n        case .doneDragging:\n            self.state = .pointing // remove associated timestamp\n        case .moving:\n            self.state = .dragging // remove associated window\n        default: ()\n        }\n    }\n\n    // Execute an action that was initiated by the observer and completed by the state keeper\n    func resizeFrameToDraggedWindowBorder(_ ratio: CGFloat) {\n        delegate?.recommendMainPaneRatio(ratio)\n    }\n\n    // Execute an action that was initiated by the observer and completed by the state keeper\n    func swapDraggedWindowWithDropzone(_ draggedWindow: Delegate.Window) {\n        delegate?.swapDraggedWindowWithDropzone(draggedWindow)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/Reliability.swift",
    "content": "//\n//  Reliability.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/8/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\n\n/// `Relatable` wraps a value with the level of confidence that the value is correct.\nenum Reliable<T> {\n    /// `reliable` means that the value is probably correct.\n    case reliable(T)\n\n    /// `unreliable` means that the value may be correct, but the returning function is not confident that the value will remain stable.\n    case unreliable(T)\n}\n\nextension Reliable: Equatable where T: Equatable {}\n"
  },
  {
    "path": "Amethyst/Model/Screen.swift",
    "content": "//\n//  Screen.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport Silica\nimport SwiftyJSON\n\n/// Generic protocol for objects acting as screens in the system.\nprotocol ScreenType: Equatable {\n    /// The list of all the screens available to the system. This is assumed to be meaningfuly ordered such that the first screen is the primary screen.\n    static var availableScreens: [Self] { get }\n\n    /// If `true` this means that each screen has its own set of spaces. If `false` there is only one set of spaces shared by all screens.\n    static var screensHaveSeparateSpaces: Bool { get }\n\n    /**\n     Descriptions of all screens taken from the underlying graphics system.\n     \n     These are used to correlate information from multiple sources.\n     */\n    static func screenDescriptions() -> [JSON]?\n\n    /**\n     The frame adjusted for app modifiers; e.g., window margin\n\n     - Parameters:\n        - disableWindowMargins: If `true`, then window margins won't be applied\n     */\n    func adjustedFrame(disableWindowMargins: Bool) -> CGRect\n\n    /// The frame adjusted to contain both the dock and the status menu.\n    func frameIncludingDockAndMenu() -> CGRect\n\n    /// The frame adjusted such that the dock and menu are not included.\n    func frameWithoutDockOrMenu() -> CGRect\n\n    /// The frame without adjustment.\n    func frame() -> CGRect\n\n    /// The opaque idenfitifer for the screen in the underlying graphics system.\n    func screenID() -> String?\n\n    /// Raises the window to the foreground.\n    func focusScreen()\n}\n\nextension ScreenType {\n    /**\n     The total height of all screens taking relative layout into account.\n     \n     Depending on the arrangement of multiple screens, it is possible to get a height that is larger than any of the individual screens. This function looks at each display frame's y-coordinates to calculate that height.\n     */\n    static func globalHeight() -> CGFloat {\n        let maxY = availableScreens.map { $0.frameIncludingDockAndMenu().maxY }.max() ?? 0\n        let minY = availableScreens.map { $0.frameIncludingDockAndMenu().minY }.min() ?? 0\n        return maxY - minY\n    }\n\n    /// The frame adjusted for app modifiers; e.g., window margins.\n    func adjustedFrame() -> CGRect {\n        return adjustedFrame(disableWindowMargins: false)\n    }\n}\n\nstruct AMScreen: ScreenType {\n    static var availableScreens: [AMScreen] { return NSScreen.screens.map { AMScreen(screen: $0) } }\n    static var screensHaveSeparateSpaces: Bool { return NSScreen.screensHaveSeparateSpaces }\n\n    let screen: NSScreen\n\n    func adjustedFrame(disableWindowMargins: Bool) -> CGRect {\n        var frame = UserConfiguration.shared.ignoreMenuBar() ? frameIncludingDockAndMenu() : frameWithoutDockOrMenu()\n\n        if UserConfiguration.shared.windowMargins() && !disableWindowMargins {\n            /* Inset for producing half of the full padding around screen as collapse only adds half of it to all windows */\n            let padding = floor(UserConfiguration.shared.windowMarginSize() / 2)\n\n            frame.origin.x += padding\n            frame.origin.y += padding\n            frame.size.width -= 2 * padding\n            frame.size.height -= 2 * padding\n        }\n\n        let windowMinimumWidth = UserConfiguration.shared.windowMinimumWidth()\n        let windowMinimumHeight = UserConfiguration.shared.windowMinimumHeight()\n\n        if windowMinimumWidth > frame.size.width {\n            frame.origin.x -= (windowMinimumWidth - frame.size.width) / 2\n            frame.size.width = windowMinimumWidth\n        }\n\n        if windowMinimumHeight > frame.size.height {\n            frame.origin.y -= (windowMinimumHeight - frame.size.height) / 2\n            frame.size.height = windowMinimumHeight\n        }\n\n         let isDisablePaddingOnBuiltinDisplay: Bool =\n             UserConfiguration.shared.disablePaddingOnBuiltinDisplay()\n         let isScreenBuiltin: boolean_t =\n             CGDisplayIsBuiltin(screen.deviceDescription[NSDeviceDescriptionKey(\"NSScreenNumber\")] as! CGDirectDisplayID)\n         if isDisablePaddingOnBuiltinDisplay && isScreenBuiltin == 1 {return frame}\n\n        let paddingTop = UserConfiguration.shared.screenPaddingTop()\n        let paddingBottom = UserConfiguration.shared.screenPaddingBottom()\n        let paddingLeft = UserConfiguration.shared.screenPaddingLeft()\n        let paddingRight = UserConfiguration.shared.screenPaddingRight()\n        frame.origin.y += paddingTop\n        frame.origin.x += paddingLeft\n        // subtract the right padding, and also any amount that we pushed the frame to the left with the left padding\n        frame.size.width -= (paddingRight + paddingLeft)\n        // subtract the bottom padding, and also any amount that we pushed the frame down with the top padding\n        frame.size.height -= (paddingBottom + paddingTop)\n\n        return frame\n    }\n\n    func frameIncludingDockAndMenu() -> CGRect {\n        return screen.frameIncludingDockAndMenu()\n    }\n\n    func frameWithoutDockOrMenu() -> CGRect {\n        return screen.frameWithoutDockOrMenu()\n    }\n\n    func frame() -> CGRect {\n        return screen.frame\n    }\n\n    func screenID() -> String? {\n        guard let managedDisplay = CGSCopyBestManagedDisplayForRect(CGSMainConnectionID(), frameIncludingDockAndMenu()) else {\n            return nil\n        }\n        return String(managedDisplay.takeRetainedValue())\n    }\n\n    func focusScreen() {\n        let screenFrame = frameIncludingDockAndMenu()\n        let mouseCursorPoint = NSPoint(x: screenFrame.midX, y: screenFrame.midY)\n        let mouseMoveEvent = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: mouseCursorPoint, mouseButton: .left)\n        mouseMoveEvent?.flags = CGEventFlags(rawValue: 0)\n        mouseMoveEvent?.post(tap: .cghidEventTap)\n    }\n\n    static func screenDescriptions() -> [JSON]? {\n        guard let cfScreenDescriptions = CGSCopyManagedDisplaySpaces(CGSMainConnectionID())?.takeRetainedValue() else {\n            return nil\n        }\n        guard let screenDescriptions = cfScreenDescriptions as NSArray as? [[String: AnyObject]] else {\n            return nil\n        }\n        return screenDescriptions.map { JSON($0) }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/Space.swift",
    "content": "//\n//  Space.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/9/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\nstruct Space: Equatable {\n    let id: CGSSpaceID\n    let type: CGSSpaceType\n    let uuid: String\n}\n"
  },
  {
    "path": "Amethyst/Model/Window.swift",
    "content": "//\n//  Window.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/10/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\nimport Silica\n\n// swiftlint:disable identifier_name\n @_silgen_name(\"GetProcessForPID\") @discardableResult\n func GetProcessForPID(_ pid: pid_t, _ psn: inout ProcessSerialNumber) -> OSStatus\n\n @_silgen_name(\"_SLPSSetFrontProcessWithOptions\") @discardableResult\n func _SLPSSetFrontProcessWithOptions(_ psn: inout ProcessSerialNumber, _ wid: UInt32, _ mode: UInt32) -> CGError\n\n @_silgen_name(\"SLPSPostEventRecordTo\") @discardableResult\n func SLPSPostEventRecordTo(_ psn: inout ProcessSerialNumber, _ bytes: inout UInt8) -> CGError\n\n let kCPSUserGenerated: UInt32 = 0x200\n// swiftlint:enable identifier_name\n\n/// Generic protocol for objects acting as windows in the system.\nprotocol WindowType: Equatable {\n    associatedtype Screen: ScreenType\n    associatedtype WindowID: Codable, Hashable\n\n    /// Returns the currently focused window of its type.\n    static func currentlyFocused() -> Self?\n\n    /**\n     Attempt to initialize a window based on a Silica element.\n     \n     Many of the accessibility APIs handle elements directly, so we need a way to convert those elements into a general window type. This is not necessarily meaningful in all cases — tests, for example, may provide window types that do not correspond to actual elements.\n     \n     - Parameters:\n        - element: The element representing a window.\n     */\n    init?(element: SIAccessibilityElement?)\n\n    /// Returns an opaque unique identifier for the window.\n    func id() -> WindowID\n\n    /// Returns the window's ID in the underlying window system.\n    func cgID() -> CGWindowID\n\n    /// Returns the window's current frame.\n    func frame() -> CGRect\n\n    /// Returns the screen, if any, that the window is currently on.\n    func screen() -> Screen?\n\n    /**\n     Sets the frame of the window with an error threshold for what constitutes a new frame.\n     \n     The tolerance for error is necessary as for performance reasons we avoid performing unnecessary frame assignments, but some windows (e.g., Terminal's windows) have some constraints on their size such that `frame` and `window.frame()` will differ by some small amount even if `frame` has been applied before. We want to treat that frame as equivalent if it is close enough so that we get the performance benefit.\n     \n     - Parameters:\n         - frame: The frame to apply.\n         - threshold: The error tolerance for what constitutes a new frame.\n     */\n\n    func setFrame(_ frame: CGRect, withThreshold threshold: CGSize)\n\n    /// Whether or not the window is currently holding focus.\n    func isFocused() -> Bool\n\n    /// The process ID of the process that owns the window.\n    func pid() -> pid_t\n\n    /**\n     The title of the window.\n     \n     - Note: Windows do not necessarily have titles so this can be `nil`.\n     */\n    func title() -> String?\n\n    /// Whether or not the window should actually be managed by Amethyst.\n    func shouldBeManaged() -> Bool\n\n    /// Whether or not the window should float by default.\n    func shouldFloat() -> Bool\n\n    /// Whether or not the window is currently active.\n    func isActive() -> Bool\n\n    /**\n     Focuses the window.\n     \n     - Returns:\n     `true` if the window was successfully focused, `false` otherwise.\n     */\n    @discardableResult func focus() -> Bool\n\n    @discardableResult func minimize() -> Bool\n\n    /**\n     Moves the window to a screen.\n     \n     This method takes into account the dimensions of the screen to ensure that the window actually fits onto it.\n     \n     - Parameters:\n        - screen: The screen to move the window to.\n     */\n    func moveScaled(to screen: Screen)\n\n    /// Whether or not the window is currently on any screen.\n    func isOnScreen() -> Bool\n\n    /**\n     Moves the window to a space.\n     \n     - Parameters:\n        - space: The index of the space.\n     */\n    func move(toSpace space: UInt)\n\n    /**\n     Moves the window to the space at an index.\n     \n     - Parameters:\n        - space: The index of the space\n     */\n    func move(toSpaceAtIndex space: UInt)\n\n    /**\n     Moves the window to a space.\n     \n     - Parameters:\n         - spaceID: The id of the space.\n     */\n    func move(toSpace spaceID: CGSSpaceID)\n}\n\nenum WindowDecodingError: Error {\n    case idNotFound\n}\n\n/**\n Final subclass of the Silica `SIWindow`.\n \n A final class is necessary for satisfying the `focusedWindow()` requirement in the `WindowType` protocol. Otherwise, as `SIWindow` is not final, the type system does not know how to constrain `Self`.\n */\nfinal class AXWindow: SIWindow {}\n\n/**\n Identifier for `AXWindow` objects.\n \n - Note:\n Decoding for this object is very inefficient. Use it sparingly.\n */\nfinal class AXWindowID: Hashable, Codable {\n    /// Coding keys.\n    private enum CodingKeys: String, CodingKey {\n        /// The pid of the process that owns the window.\n        case pid\n\n        /// The CoreGraphics id for the window.\n        case windowID\n    }\n\n    private let window: AXWindow\n\n    /// Equality for window IDs is based on the underlying CoreGraphics id and the owning pid, which (mostly) uniquely identifies a window.\n    static func == (lhs: AXWindowID, rhs: AXWindowID) -> Bool {\n        return lhs.window.pid() == rhs.window.pid() && lhs.window.windowID() == rhs.window.windowID()\n    }\n\n    func hash(into hasher: inout Hasher) {\n        hasher.combine(window.pid())\n        hasher.combine(window.windowID())\n    }\n\n    fileprivate init(window: AXWindow) {\n        self.window = window\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        let pid = try container.decode(pid_t.self, forKey: .pid)\n        let windowID = try container.decode(CGWindowID.self, forKey: .windowID)\n\n        guard let application = SIApplication(pid: pid) else {\n            throw WindowDecodingError.idNotFound\n        }\n\n        let windows: [SIWindow] = application.windows()\n\n        guard let window = windows.first(where: { $0.windowID() == windowID }) else {\n            throw WindowDecodingError.idNotFound\n        }\n\n        self.window = AXWindow(axElement: window.axElementRef)\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.container(keyedBy: CodingKeys.self)\n        try container.encode(window.pid(), forKey: .pid)\n        try container.encode(window.windowID(), forKey: .windowID)\n    }\n}\n\nextension AXWindowID: CustomStringConvertible {\n    var description: String {\n        return \"\\(window.title() ?? \"unknown\") (\\(window.windowID()))\"\n    }\n}\n\n/// Conformance of `AXWindow` as an Amethyst window.\nextension AXWindow: WindowType {\n    typealias Screen = AMScreen\n    typealias WindowID = AXWindowID\n\n    /**\n     Returns the currently focused window.\n     \n     - Returns:\n     The currently focused window as an `AXWindow`.\n     */\n    static func currentlyFocused() -> AXWindow? {\n        return SIWindow.focused().flatMap { AXWindow(axElement: $0.axElementRef) }\n    }\n\n    /**\n     The Silica initializer is not failable because it can always assume it has a reference to an ax element. The window type in general does not make that assumption and thus has a failable initializer. This just ports one into the other.\n     \n     - Parameters:\n        - element: The element representing a window.\n     */\n    convenience init?(element: SIAccessibilityElement?) {\n        guard let axElementRef = element?.axElementRef else {\n            return nil\n        }\n\n        self.init(axElement: axElementRef)\n\n        if string(forKey: \"AXRole\" as CFString) != \"AXWindow\" {\n            return nil\n        }\n    }\n\n    func id() -> WindowID {\n        return AXWindowID(window: self)\n    }\n\n    func cgID() -> CGWindowID {\n        return windowID()\n    }\n\n    func screen() -> AMScreen? {\n        let nsScreen: NSScreen? = screen()\n        return nsScreen.flatMap { AMScreen(screen: $0) }\n    }\n\n    func pid() -> pid_t {\n        // Some window operations can surface elements owned by a helper process.\n        // Use AXParent's PID when available so identity checks stay stable.\n        return forKey(\"AXParent\" as CFString)?.processIdentifier() ?? processIdentifier()\n    }\n\n    /**\n     Whether or not the window should actually be managed by Amethyst.\n     \n     In this case the window must be movable and be a standard window.\n     */\n    func shouldBeManaged() -> Bool {\n        guard isMovable() else {\n            return false\n        }\n\n        guard let subrole = string(forKey: kAXSubroleAttribute as CFString), subrole == kAXStandardWindowSubrole as String else {\n            return false\n        }\n\n        return true\n    }\n\n    func shouldFloat() -> Bool {\n        let userConfiguration = UserConfiguration.shared\n        let frame = self.frame()\n        let threshold = userConfiguration.smallWindowSize()\n\n        if userConfiguration.floatSmallWindows() && frame.size.width < threshold && frame.size.height < threshold {\n            return true\n        }\n\n        return false\n    }\n\n    func isFocused() -> Bool {\n        guard let focused = AXWindow.currentlyFocused() else {\n            return false\n        }\n\n        return isEqual(to: focused)\n    }\n\n    /**\n     Focuses the window.\n     \n     This handles focusing and also moves the cursor to the window's frame if mouse-follows-focus is enabled.\n     \n     - Returns:\n     `true` if the window was successfully focused, `false` otherwise.\n     \n     - Description:\n     What a mess. See: https://github.com/Hammerspoon/hammerspoon/issues/370#issuecomment-545545468\n     */\n    @discardableResult override func focus() -> Bool {\n        let pid = self.pid()\n        var wid = self.cgID()\n        var psn = ProcessSerialNumber()\n        let status = GetProcessForPID(pid, &psn)\n\n        guard status == noErr else {\n            return false\n        }\n\n        var cgStatus = _SLPSSetFrontProcessWithOptions(&psn, wid, kCPSUserGenerated)\n\n        guard cgStatus == .success else {\n            return false\n        }\n\n        for byte in [0x01, 0x02] {\n            var bytes = [UInt8](repeating: 0, count: 0xf8)\n            bytes[0x04] = 0xF8\n            bytes[0x08] = UInt8(byte)\n            bytes[0x3a] = 0x10\n            memcpy(&bytes[0x3c], &wid, MemoryLayout<UInt32>.size)\n            memset(&bytes[0x20], 0xFF, 0x10)\n            cgStatus = bytes.withUnsafeMutableBufferPointer { pointer in\n                return SLPSPostEventRecordTo(&psn, &pointer.baseAddress!.pointee)\n            }\n            guard cgStatus == .success else {\n                return false\n            }\n        }\n\n        guard super.raise() else {\n            return false\n        }\n\n        guard UserConfiguration.shared.mouseFollowsFocus() else {\n            return true\n        }\n\n        let windowFrame = frame()\n        let mouseCursorPoint = NSPoint(x: windowFrame.midX, y: windowFrame.midY)\n        guard let mouseMoveEvent = CGEvent(mouseEventSource: nil, mouseType: .mouseMoved, mouseCursorPosition: mouseCursorPoint, mouseButton: .left) else {\n            return true\n        }\n        mouseMoveEvent.flags = CGEventFlags(rawValue: 0)\n        mouseMoveEvent.post(tap: CGEventTapLocation.cghidEventTap)\n\n        return true\n    }\n\n    @discardableResult func minimize() -> Bool {\n        super.minimize()\n        return isWindowMinimized()\n    }\n\n    func moveScaled(to screen: Screen) {\n        let screenFrame = screen.frameWithoutDockOrMenu()\n        let currentFrame = frame()\n        var scaledFrame = currentFrame\n\n        if scaledFrame.width > screenFrame.width {\n            scaledFrame.size.width = screenFrame.width\n        }\n\n        if scaledFrame.height > screenFrame.height {\n            scaledFrame.size.height = screenFrame.height\n        }\n\n        if scaledFrame != currentFrame {\n            setFrame(scaledFrame)\n        }\n\n        move(to: screen.screen)\n    }\n\n    func move(toSpaceAtIndex space: UInt) {\n        super.move(toSpace: space)\n    }\n\n    func move(toSpace spaceID: CGSSpaceID) {\n    }\n}\n\nextension AXWindow {\n    override var description: String {\n        return \"\\(super.description) (\\(cgID()))\"\n    }\n}\n"
  },
  {
    "path": "Amethyst/Model/WindowsInformation.swift",
    "content": "//\n//  WindowsInformation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ApplicationServices\nimport Foundation\nimport Silica\n\nextension CGRect {\n    func approximatelyEqual(to otherRect: CGRect, within tolerance: CGRect) -> Bool {\n        return abs(origin.x - otherRect.origin.x) < tolerance.origin.x &&\n        abs(origin.y - otherRect.origin.y) < tolerance.origin.y &&\n        abs(width - otherRect.width) < tolerance.width &&\n        abs(height - otherRect.height) < tolerance.height\n    }\n}\n\nstruct WindowsInformation<Window: WindowType> {\n    let ids: Set<CGWindowID>\n    let descriptions: CGWindowsInfo<Window>?\n\n    init?(windows: [Window]) {\n        guard let descriptions = CGWindowsInfo<Window>(options: .optionOnScreenOnly, windowID: CGWindowID(0)) else {\n            return nil\n        }\n\n        self.ids = Set(windows.map { $0.cgID() })\n        self.descriptions = descriptions\n    }\n}\n\nextension WindowsInformation {\n    // convert Window objects to CGWindowIDs.\n    // additionally, return the full set of window descriptions (which is unsorted and may contain extra windows)\n    fileprivate static func windowInformation(_ windows: [Window]) -> (IDs: Set<CGWindowID>, descriptions: [[String: AnyObject]]?) {\n        let ids = Set(windows.map { $0.cgID() })\n        return (IDs: ids, descriptions: CGWindowsInfo<Window>(options: .optionOnScreenOnly, windowID: CGWindowID(0))?.descriptions)\n    }\n\n    fileprivate static func onScreenWindowsAtPoint(_ point: CGPoint,\n                                                   withIDs windowIDs: Set<CGWindowID>,\n                                                   withDescriptions windowDescriptions: [[String: AnyObject]]) -> [[String: AnyObject]] {\n        var ret: [[String: AnyObject]] = []\n\n        // build a list of windows at this point\n        for windowDescription in windowDescriptions {\n            guard let windowID = (windowDescription[kCGWindowNumber as String] as? NSNumber).flatMap({ CGWindowID($0.intValue) }),\n                windowIDs.contains(windowID) else {\n                continue\n            }\n\n            // only consider windows with bounds\n            guard let windowFrameDictionary = windowDescription[kCGWindowBounds as String] as? [String: Any] else {\n                continue\n            }\n\n            // only consider window bounds that contain the given point\n            let windowFrame = CGRect(dictionaryRepresentation: windowFrameDictionary as CFDictionary)!\n            guard windowFrame.contains(point) else {\n                continue\n            }\n            ret.append(windowDescription)\n        }\n\n        return ret\n    }\n\n    // if there are several windows at a given screen point, take the top one\n    static func topWindowForScreenAtPoint(_ point: CGPoint, withWindows windows: [Window]) -> Window? {\n        let (ids, maybeWindowDescriptions) = windowInformation(windows)\n        guard let windowDescriptions = maybeWindowDescriptions, !windowDescriptions.isEmpty else {\n            return nil\n        }\n\n        let windowsAtPoint = onScreenWindowsAtPoint(point, withIDs: ids, withDescriptions: windowDescriptions)\n\n        guard !windowsAtPoint.isEmpty else {\n            return nil\n        }\n\n        guard windowsAtPoint.count > 1 else {\n            return windowInWindows(windows, withCGWindowDescription: windowsAtPoint[0])\n        }\n\n        var windowToFocus: [String: AnyObject]?\n        var minCount = windowDescriptions.count\n        for windowDescription in windowsAtPoint {\n            guard let windowID = windowDescription[kCGWindowNumber as String] as? NSNumber else {\n                continue\n            }\n\n            guard let windowsAboveWindow = CGWindowsInfo<Window>(options: .optionOnScreenAboveWindow, windowID: windowID.uint32Value) else {\n                continue\n            }\n\n            if windowsAboveWindow.descriptions.count < minCount {\n                windowToFocus = windowDescription\n                minCount = windowsAboveWindow.descriptions.count\n            }\n        }\n\n        guard let windowDictionaryToFocus = windowToFocus else {\n            return nil\n        }\n\n        return windowInWindows(windows, withCGWindowDescription: windowDictionaryToFocus)\n    }\n\n    // get the first window at a certain point, excluding one specific window from consideration\n    static func alternateWindowForScreenAtPoint(_ point: CGPoint, withWindows windows: [Window], butNot ignoreWindow: Window?) -> Window? {\n        // only consider windows on this screen\n        let (ids, maybeWindowDescriptions) = windowInformation(windows)\n        guard let windowDescriptions = maybeWindowDescriptions, !windowDescriptions.isEmpty else {\n            return nil\n        }\n\n        let windowsAtPoint = onScreenWindowsAtPoint(point, withIDs: ids, withDescriptions: windowDescriptions)\n\n        for windowDescription in windowsAtPoint {\n            if let window = windowInWindows(windows, withCGWindowDescription: windowDescription) {\n                if let ignored = ignoreWindow, window != ignored {\n                    return window\n                }\n            }\n        }\n\n        return nil\n    }\n\n    // find a window based on its window description within an array of Window objects\n    static func windowInWindows(_ windows: [Window], withCGWindowDescription windowDescription: [String: AnyObject]) -> Window? {\n        let potentialWindows = windows.filter {\n            guard let windowOwnerPID = windowDescription[kCGWindowOwnerPID as String] as? NSNumber else {\n                return false\n            }\n\n            guard windowOwnerPID.int32Value == $0.pid() else {\n                return false\n            }\n\n            guard let boundsDictionary = windowDescription[kCGWindowBounds as String] as? [String: Any] else {\n                return false\n            }\n\n            let windowFrame = CGRect(dictionaryRepresentation: boundsDictionary as CFDictionary)!\n\n            guard windowFrame.equalTo($0.frame()) else {\n                return false\n            }\n\n            return true\n        }\n\n        guard potentialWindows.count > 1 else {\n            return potentialWindows.first\n        }\n\n        return potentialWindows.first {\n            guard let describedTitle = windowDescription[kCGWindowName as String] as? String else {\n                return false\n            }\n\n            let describedOwner = windowDescription[kCGWindowOwnerName as String] as? String\n            let describedOwnedTitle = describedOwner.flatMap { \"\\(describedTitle) - \\($0)\" }\n\n            return describedTitle == $0.title() || describedOwnedTitle == $0.title()\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Preferences/DebugPreferencesViewController.swift",
    "content": "//\n//  DebugPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\nimport Foundation\n\nclass DebugPreferencesViewController: NSViewController {}\n"
  },
  {
    "path": "Amethyst/Preferences/DebugPreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"15702\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"15702\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"DebugPreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"view\" destination=\"gFQ-ju-I7J\" id=\"le4-d2-9j1\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customView id=\"gFQ-ju-I7J\" userLabel=\"Debug Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"548\" height=\"118\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <button focusRingType=\"none\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZJk-hd-85X\">\n                    <rect key=\"frame\" x=\"192\" y=\"71\" width=\"106\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"About layouts\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" focusRingType=\"none\" inset=\"2\" id=\"IhC-06-Coe\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <connections>\n                            <binding destination=\"eeU-g1-X9P\" name=\"value\" keyPath=\"values.debug-layout-info\" id=\"wXJ-rV-WJS\"/>\n                        </connections>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"eeU-g1-X9P\" name=\"value\" keyPath=\"values.debug-layout-info\" id=\"hGW-sl-LHl\"/>\n                    </connections>\n                </button>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"SqR-cf-d23\">\n                    <rect key=\"frame\" x=\"78\" y=\"72\" width=\"110\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"Show debug info:\" id=\"ruf-97-sRi\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Obp-aZ-loC\">\n                    <rect key=\"frame\" x=\"129\" y=\"48\" width=\"59\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"Updates:\" id=\"aY2-1D-UHQ\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ccm-Kv-UDb\">\n                    <rect key=\"frame\" x=\"192\" y=\"47\" width=\"166\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Get development builds\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"NTI-ED-2OH\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"eeU-g1-X9P\" name=\"value\" keyPath=\"values.use-canary-build\" id=\"X6W-b5-aB9\"/>\n                    </connections>\n                </button>\n                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZpS-zQ-snc\">\n                    <rect key=\"frame\" x=\"192\" y=\"31\" width=\"240\" height=\"14\"/>\n                    <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" title=\"You must relaunch for changes to take effect\" id=\"KfR-Yz-Cyo\">\n                        <font key=\"font\" metaFont=\"message\" size=\"11\"/>\n                        <color key=\"textColor\" name=\"secondaryLabelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n            </subviews>\n            <constraints>\n                <constraint firstItem=\"ZJk-hd-85X\" firstAttribute=\"centerY\" secondItem=\"SqR-cf-d23\" secondAttribute=\"centerY\" id=\"AO0-O9-SDm\"/>\n                <constraint firstItem=\"ccm-Kv-UDb\" firstAttribute=\"leading\" secondItem=\"Obp-aZ-loC\" secondAttribute=\"trailing\" constant=\"8\" id=\"Imf-jN-s5E\"/>\n                <constraint firstItem=\"Obp-aZ-loC\" firstAttribute=\"top\" secondItem=\"SqR-cf-d23\" secondAttribute=\"bottom\" constant=\"8\" id=\"aD9-tN-NZZ\"/>\n                <constraint firstItem=\"ZpS-zQ-snc\" firstAttribute=\"leading\" secondItem=\"ccm-Kv-UDb\" secondAttribute=\"leading\" id=\"aGo-GY-M8k\"/>\n                <constraint firstItem=\"Obp-aZ-loC\" firstAttribute=\"trailing\" secondItem=\"SqR-cf-d23\" secondAttribute=\"trailing\" id=\"iQt-Di-918\"/>\n                <constraint firstItem=\"ZJk-hd-85X\" firstAttribute=\"leading\" secondItem=\"SqR-cf-d23\" secondAttribute=\"trailing\" constant=\"8\" id=\"kLt-s8-MN2\"/>\n                <constraint firstItem=\"SqR-cf-d23\" firstAttribute=\"top\" secondItem=\"gFQ-ju-I7J\" secondAttribute=\"top\" constant=\"30\" id=\"nAy-P9-niV\"/>\n                <constraint firstItem=\"ZpS-zQ-snc\" firstAttribute=\"top\" secondItem=\"ccm-Kv-UDb\" secondAttribute=\"bottom\" constant=\"4\" id=\"ncb-Nv-snX\"/>\n                <constraint firstItem=\"ccm-Kv-UDb\" firstAttribute=\"centerY\" secondItem=\"Obp-aZ-loC\" secondAttribute=\"centerY\" id=\"oRf-Xv-P3N\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"SqR-cf-d23\" secondAttribute=\"trailing\" constant=\"362\" id=\"wLG-jc-7HM\"/>\n            </constraints>\n            <point key=\"canvasLocation\" x=\"561\" y=\"24\"/>\n        </customView>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"eeU-g1-X9P\"/>\n    </objects>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/FloatingPreferencesViewController.swift",
    "content": "//\n//  GeneralPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\n\nclass FloatingPreferencesViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {\n    private var floatingBundles: [FloatingBundle] {\n        return arrayController.arrangedObjects as! [FloatingBundle]\n    }\n\n    @IBOutlet var floatingTableView: NSTableView!\n    @IBOutlet var windowTitlesTableView: NSTableView!\n    @IBOutlet var windowTitlesCoverView: NSView!\n    @IBOutlet var arrayController: NSArrayController!\n\n    override func awakeFromNib() {\n        super.awakeFromNib()\n\n        windowTitlesCoverView.wantsLayer = true\n        windowTitlesCoverView.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.3).cgColor\n    }\n\n    override func viewWillAppear() {\n        super.viewWillAppear()\n\n        arrayController?.content = NSMutableArray(array: UserConfiguration.shared.floatingBundles())\n        arrayController?.setSelectionIndexes(IndexSet())\n    }\n\n    @IBAction func addFloatingApplication(_ sender: NSButton) {\n        let layoutMenu = NSMenu(title: \"\")\n        let selectMenuItem = NSMenuItem(title: \"Select from applications...\", action: #selector(selectFloatingApplication(_:)), keyEquivalent: \"\")\n        let manualMenuItem = NSMenuItem(title: \"Manually enter identifier...\", action: #selector(manuallyEnterFloatingApplication(_:)), keyEquivalent: \"\")\n\n        layoutMenu.addItem(selectMenuItem)\n        layoutMenu.addItem(manualMenuItem)\n\n        let frame = sender.frame\n        let menuOrigin = sender.superview!.convert(NSPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height + 40), to: nil)\n\n        let event = NSEvent.mouseEvent(\n            with: NSEvent.EventType.leftMouseDown,\n            location: menuOrigin,\n            modifierFlags: [],\n            timestamp: 0,\n            windowNumber: sender.window!.windowNumber,\n            context: sender.window!.graphicsContext,\n            eventNumber: 0,\n            clickCount: 1,\n            pressure: 1\n        )\n\n        NSMenu.popUpContextMenu(layoutMenu, with: event!, for: sender)\n    }\n\n    @objc func selectFloatingApplication(_ sender: AnyObject) {\n        let openPanel = NSOpenPanel()\n        let applicationDirectories = FileManager.default.urls(for: .applicationDirectory, in: .localDomainMask)\n\n        openPanel.canChooseFiles = true\n        openPanel.canChooseDirectories = false\n        openPanel.allowsMultipleSelection = true\n        openPanel.allowedFileTypes = [\"app\"]\n        openPanel.prompt = \"Select\"\n        openPanel.directoryURL = applicationDirectories.first\n\n        guard case openPanel.runModal() = NSApplication.ModalResponse.OK else {\n            return\n        }\n\n        for applicationURL in openPanel.urls {\n            guard let applicationBundleIdentifier = Bundle(url: applicationURL)?.bundleIdentifier else {\n                continue\n            }\n\n            addFloatingApplicationBundleIdentifier(applicationBundleIdentifier)\n        }\n    }\n\n    @objc func manuallyEnterFloatingApplication(_ sender: AnyObject) {\n        guard let window = view.window else {\n            return\n        }\n\n        let alert = NSAlert()\n        let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))\n        alert.accessoryView = field\n        alert.icon = nil\n        alert.messageText = \"Application Bundle ID\"\n        alert.addButton(withTitle: \"Add\")\n        alert.addButton(withTitle: \"Cancel\")\n        alert.beginSheetModal(for: window) { [weak self] response in\n            guard response == .alertFirstButtonReturn else {\n                return\n            }\n\n            self?.addFloatingApplicationBundleIdentifier(field.stringValue)\n        }\n    }\n\n    private func addFloatingApplicationBundleIdentifier(_ bundleIdentifier: String) {\n        let floatingBundle = FloatingBundle(id: bundleIdentifier, windowTitles: [])\n        arrayController.addObject(floatingBundle)\n\n        UserConfiguration.shared.setFloatingBundles(self.floatingBundles)\n    }\n\n    @IBAction func removeFloatingApplication(_ sender: AnyObject) {\n        guard let floatingTableView = floatingTableView else {\n            return\n        }\n\n        guard floatingTableView.selectedRow < floatingBundles.count, floatingTableView.selectedRow != NSTableView.noRowSelectedIndex else {\n            return\n        }\n\n        arrayController.remove(atArrangedObjectIndex: floatingTableView.selectedRow)\n\n        UserConfiguration.shared.setFloatingBundles(self.floatingBundles)\n    }\n\n    @IBAction func addWindowTitle(_ sender: AnyObject) {\n        guard let window = view.window else {\n            return\n        }\n\n        let alert = NSAlert()\n        let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))\n        alert.accessoryView = field\n        alert.icon = nil\n        alert.messageText = \"Window Title\"\n        alert.addButton(withTitle: \"Add\")\n        alert.addButton(withTitle: \"Cancel\")\n        alert.beginSheetModal(for: window) { [weak self] response in\n            guard response == .alertFirstButtonReturn else {\n                return\n            }\n\n            self?.addWindowTitleToSelectedApplication(field.stringValue)\n        }\n    }\n\n    @IBAction func removeWindowTitle(_ sender: AnyObject) {\n        let selection = arrayController.selection as AnyObject\n\n        guard let id = selection.value(forKey: \"id\") as? String, let windowTitles = selection.value(forKey: \"windowTitles\") as? [String] else {\n            return\n        }\n\n        guard windowTitlesTableView.selectedRow < windowTitles.count, windowTitlesTableView.selectedRow != NSTableView.noRowSelectedIndex else {\n            return\n        }\n\n        let title = windowTitles[windowTitlesTableView.selectedRow]\n        let updatedBundle = FloatingBundle(id: id, windowTitles: windowTitles.filter { $0 != title })\n        floatingBundles.firstIndex { $0.id == id }.flatMap { index in\n            arrayController.remove(atArrangedObjectIndex: index)\n            arrayController.insert(updatedBundle, atArrangedObjectIndex: index)\n        }\n\n        UserConfiguration.shared.setFloatingBundles(self.floatingBundles)\n    }\n\n    private func addWindowTitleToSelectedApplication(_ title: String) {\n        let selection = arrayController.selection as AnyObject\n\n        guard let id = selection.value(forKey: \"id\") as? String, let windowTitles = selection.value(forKey: \"windowTitles\") as? [String] else {\n            return\n        }\n\n        let updatedBundle = FloatingBundle(id: id, windowTitles: windowTitles + [title])\n        floatingBundles.firstIndex { $0.id == id }.flatMap { index in\n            arrayController.remove(atArrangedObjectIndex: index)\n            arrayController.insert(updatedBundle, atArrangedObjectIndex: index)\n        }\n\n        UserConfiguration.shared.setFloatingBundles(self.floatingBundles)\n    }\n}\n\n@objc(FloatingBlacklistIntBooleanTransformer) class FloatingBlacklistIntBooleanTransformer: ValueTransformer {\n    override class func transformedValueClass() -> AnyClass {\n        return NSNumber.self\n    }\n\n    override func transformedValue(_ value: Any?) -> Any? {\n        guard let number = value as? Int else {\n            return nil\n        }\n\n        switch number {\n        case 0:\n            return true\n        case 1:\n            return false\n        default:\n            return nil\n        }\n    }\n\n    override func reverseTransformedValue(_ value: Any?) -> Any? {\n        guard let boolean = value as? Bool else {\n            return nil\n        }\n\n        return boolean ? 0 : 1\n    }\n}\n\n@objc(AllWindowsHiddenTitlesCountTransformer) class AllWindowsHiddenTitlesCountTransformer: ValueTransformer {\n    override class func transformedValueClass() -> AnyClass {\n        return NSNumber.self\n    }\n\n    override func transformedValue(_ value: Any?) -> Any? {\n        guard let count = value as? Int else {\n            return false\n        }\n\n        return count != 0\n    }\n}\n\n@objc(ApplicationSelectedTransformer) class ApplicationSelectedTransformer: ValueTransformer {\n    override class func transformedValueClass() -> AnyClass {\n        return NSNumber.self\n    }\n\n    override func transformedValue(_ value: Any?) -> Any? {\n        guard let index = value as? Int else {\n            return false\n        }\n\n        return index != NSNotFound\n    }\n}\n\n@objc(NoApplicationSelectedTransformer) class NoApplicationSelectedTransformer: ValueTransformer {\n    override class func transformedValueClass() -> AnyClass {\n        return NSNumber.self\n    }\n\n    override func transformedValue(_ value: Any?) -> Any? {\n        guard let index = value as? Int else {\n            return false\n        }\n\n        return index == NSNotFound\n    }\n}\n"
  },
  {
    "path": "Amethyst/Preferences/FloatingPreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"24412\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"24412\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"FloatingPreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"arrayController\" destination=\"1St-Zh-cQw\" id=\"2yg-BF-AeG\"/>\n                <outlet property=\"floatingTableView\" destination=\"0sm-tb-Rkn\" id=\"4dS-u4-ZLp\"/>\n                <outlet property=\"view\" destination=\"LsO-9M-hEw\" id=\"7dy-Su-MSH\"/>\n                <outlet property=\"windowTitlesCoverView\" destination=\"bIY-NG-cWh\" id=\"qx8-0h-vlE\"/>\n                <outlet property=\"windowTitlesTableView\" destination=\"a4Q-Mp-SKh\" id=\"i9h-k8-fls\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customView id=\"LsO-9M-hEw\" userLabel=\"Floating Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"548\" height=\"447\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <scrollView focusRingType=\"none\" horizontalCompressionResistancePriority=\"250\" borderType=\"line\" autohidesScrollers=\"YES\" horizontalLineScroll=\"19\" horizontalPageScroll=\"10\" verticalLineScroll=\"19\" verticalPageScroll=\"10\" usesPredominantAxisScrolling=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"U6O-Ka-pXq\" userLabel=\"Application IDs\">\n                    <rect key=\"frame\" x=\"30\" y=\"30\" width=\"180\" height=\"210\"/>\n                    <clipView key=\"contentView\" focusRingType=\"none\" drawsBackground=\"NO\" id=\"RT8-fN-JDO\">\n                        <rect key=\"frame\" x=\"1\" y=\"1\" width=\"178\" height=\"208\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView focusRingType=\"none\" verticalHuggingPriority=\"750\" allowsExpansionToolTips=\"YES\" columnAutoresizingStyle=\"lastColumnOnly\" multipleSelection=\"NO\" autosaveColumns=\"NO\" typeSelect=\"NO\" id=\"0sm-tb-Rkn\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"178\" height=\"184\"/>\n                                <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                <size key=\"intercellSpacing\" width=\"3\" height=\"2\"/>\n                                <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                <color key=\"gridColor\" name=\"gridColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                <tableColumns>\n                                    <tableColumn editable=\"NO\" width=\"137\" minWidth=\"40\" maxWidth=\"1000\" id=\"IPH-Pg-lu9\">\n                                        <tableHeaderCell key=\"headerCell\" lineBreakMode=\"truncatingTail\" borderStyle=\"border\" alignment=\"left\">\n                                            <color key=\"textColor\" name=\"headerTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"backgroundColor\" white=\"0.33333298560000002\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                                        </tableHeaderCell>\n                                        <textFieldCell key=\"dataCell\" lineBreakMode=\"truncatingTail\" selectable=\"YES\" editable=\"YES\" alignment=\"left\" title=\"Text Cell\" id=\"FXn-uF-EOx\">\n                                            <font key=\"font\" metaFont=\"system\"/>\n                                            <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                        </textFieldCell>\n                                        <tableColumnResizingMask key=\"resizingMask\" resizeWithTable=\"YES\"/>\n                                        <connections>\n                                            <binding destination=\"1St-Zh-cQw\" name=\"value\" keyPath=\"arrangedObjects.id\" id=\"OmG-7Y-bWv\"/>\n                                        </connections>\n                                    </tableColumn>\n                                </tableColumns>\n                                <connections>\n                                    <binding destination=\"1St-Zh-cQw\" name=\"content\" keyPath=\"arrangedObjects\" id=\"eHa-Oe-Cj7\"/>\n                                    <binding destination=\"1St-Zh-cQw\" name=\"selectionIndexes\" keyPath=\"selectionIndexes\" previousBinding=\"eHa-Oe-Cj7\" id=\"MbW-FE-OuA\"/>\n                                    <outlet property=\"delegate\" destination=\"-2\" id=\"ggq-8n-BWg\"/>\n                                </connections>\n                            </tableView>\n                        </subviews>\n                        <nil key=\"backgroundColor\"/>\n                    </clipView>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"180\" id=\"lyT-Cm-gS8\"/>\n                    </constraints>\n                    <edgeInsets key=\"contentInsets\" left=\"0.0\" right=\"0.0\" top=\"0.0\" bottom=\"25\"/>\n                    <scroller key=\"horizontalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"YES\" id=\"IwH-cx-YkX\">\n                        <rect key=\"frame\" x=\"-7\" y=\"-14\" width=\"0.0\" height=\"15\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </scroller>\n                    <scroller key=\"verticalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"NO\" id=\"eCG-Tq-r9K\">\n                        <rect key=\"frame\" x=\"-14\" y=\"-7\" width=\"15\" height=\"0.0\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </scroller>\n                </scrollView>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZH0-nz-Yiv\" userLabel=\"Add App ID\">\n                    <rect key=\"frame\" x=\"31\" y=\"29.5\" width=\"27.5\" height=\"29\"/>\n                    <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSAddTemplate\" imagePosition=\"only\" alignment=\"center\" state=\"on\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"ok9-1r-DwF\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"27\" id=\"u68-Qg-UQ0\"/>\n                    </constraints>\n                    <connections>\n                        <action selector=\"addFloatingApplication:\" target=\"-2\" id=\"2LL-EJ-b2i\"/>\n                    </connections>\n                </button>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"cfp-8K-Cdt\" userLabel=\"Remove App ID\">\n                    <rect key=\"frame\" x=\"59\" y=\"34\" width=\"27.5\" height=\"20\"/>\n                    <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSRemoveTemplate\" imagePosition=\"overlaps\" alignment=\"center\" state=\"on\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"Xif-lH-IK0\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"27\" id=\"ZjR-Vq-3eu\"/>\n                    </constraints>\n                    <connections>\n                        <action selector=\"removeFloatingApplication:\" target=\"-2\" id=\"BRc-C0-JNy\"/>\n                    </connections>\n                </button>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9hq-U6-t9B\">\n                    <rect key=\"frame\" x=\"194\" y=\"401\" width=\"143\" height=\"16\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Float small windows\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"phj-M8-HfQ\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.float-small-windows\" id=\"ahP-PY-5hW\"/>\n                    </connections>\n                </button>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Mqz-bw-Kwm\">\n                    <rect key=\"frame\" x=\"133\" y=\"401\" width=\"55\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"General:\" id=\"OZb-cJ-WUP\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sWs-Th-001\" userLabel=\"Small Window Size Label\">\n                    <rect key=\"frame\" x=\"340\" y=\"401\" width=\"83\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"smaller than\" id=\"sWs-Th-002\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.float-small-windows\" id=\"sWs-Th-003\"/>\n                    </connections>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sWs-Th-004\" userLabel=\"Small Window Size Input\">\n                    <rect key=\"frame\" x=\"428\" y=\"399\" width=\"50\" height=\"22\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"sWs-Th-005\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"0\" id=\"sWs-Th-006\">\n                            <real key=\"minimum\" value=\"1\"/>\n                            <real key=\"maximum\" value=\"10000\"/>\n                        </numberFormatter>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.small-window-size\" id=\"sWs-Th-007\"/>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.float-small-windows\" id=\"sWs-Th-008\"/>\n                    </connections>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sWs-Th-009\" userLabel=\"Small Window Size Stepper\">\n                    <rect key=\"frame\" x=\"478\" y=\"396\" width=\"19\" height=\"27\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"50\" minValue=\"1\" maxValue=\"10000\" doubleValue=\"500\" id=\"sWs-Th-010\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.small-window-size\" id=\"sWs-Th-011\"/>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.float-small-windows\" id=\"sWs-Th-012\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sWs-Th-013\" userLabel=\"px Label\">\n                    <rect key=\"frame\" x=\"499\" y=\"401\" width=\"20\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"px\" id=\"sWs-Th-014\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.float-small-windows\" id=\"sWs-Th-015\"/>\n                    </connections>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Q7b-oi-oDI\">\n                    <rect key=\"frame\" x=\"192\" y=\"369\" width=\"294\" height=\"28\"/>\n                    <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" title=\"This option is recommended to avoid tiling dialogs for some applications.\" id=\"GHB-Qd-gAx\">\n                        <font key=\"font\" metaFont=\"menu\" size=\"11\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <box horizontalHuggingPriority=\"248\" verticalHuggingPriority=\"750\" boxType=\"separator\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"2vs-LF-ePe\">\n                    <rect key=\"frame\" x=\"174\" y=\"336\" width=\"200\" height=\"5\"/>\n                </box>\n                <popUpButton verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9ag-lu-Zd7\" userLabel=\"Blacklist Toggle\">\n                    <rect key=\"frame\" x=\"30\" y=\"298\" width=\"488\" height=\"24\"/>\n                    <popUpButtonCell key=\"cell\" type=\"push\" title=\"Automatically float applications listed\" bezelStyle=\"rounded\" alignment=\"left\" lineBreakMode=\"truncatingTail\" borderStyle=\"borderAndBezel\" imageScaling=\"proportionallyDown\" inset=\"2\" autoenablesItems=\"NO\" altersStateOfSelectedItem=\"NO\" selectedItem=\"dBv-SS-kCX\" id=\"zLN-Ws-Prr\">\n                        <behavior key=\"behavior\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"message\"/>\n                        <menu key=\"menu\" autoenablesItems=\"NO\" id=\"4pZ-9T-T6n\">\n                            <items>\n                                <menuItem title=\"Automatically float applications listed\" id=\"dBv-SS-kCX\" userLabel=\"Blacklist\"/>\n                                <menuItem title=\"Automatically float all applications except those listed\" id=\"zy1-GY-HeS\" userLabel=\"Whitelist\">\n                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                </menuItem>\n                            </items>\n                        </menu>\n                    </popUpButtonCell>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"selectedIndex\" keyPath=\"values.floating-is-blacklist\" id=\"Eho-vt-fZx\">\n                            <dictionary key=\"options\">\n                                <string key=\"NSValueTransformerName\">FloatingBlacklistIntBooleanTransformer</string>\n                            </dictionary>\n                        </binding>\n                    </connections>\n                </popUpButton>\n                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Dvu-vy-f2R\">\n                    <rect key=\"frame\" x=\"32\" y=\"248\" width=\"492\" height=\"42\"/>\n                    <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" id=\"ynm-bG-Bg4\">\n                        <font key=\"font\" metaFont=\"menu\" size=\"11\"/>\n                        <string key=\"title\">Floating an application means that windows from that application will automatically be treated as floating. Windows can still manually be tiled or floated by toggling their floating state.</string>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <box horizontalCompressionResistancePriority=\"1000\" title=\"Window Title Restrictions\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"iHL-Zc-7k5\">\n                    <rect key=\"frame\" x=\"218\" y=\"30\" width=\"300\" height=\"214\"/>\n                    <view key=\"contentView\" id=\"LKC-IK-MmH\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"300\" height=\"202\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <customView translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ix5-Vi-yZw\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"300\" height=\"202\"/>\n                                <subviews>\n                                    <scrollView focusRingType=\"none\" borderType=\"line\" autohidesScrollers=\"YES\" horizontalLineScroll=\"19\" horizontalPageScroll=\"10\" verticalLineScroll=\"19\" verticalPageScroll=\"10\" usesPredominantAxisScrolling=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hb8-rU-qD8\" userLabel=\"Window Titles\">\n                                        <rect key=\"frame\" x=\"20\" y=\"70\" width=\"260\" height=\"112\"/>\n                                        <clipView key=\"contentView\" focusRingType=\"none\" drawsBackground=\"NO\" id=\"6un-vZ-A29\">\n                                            <rect key=\"frame\" x=\"1\" y=\"1\" width=\"258\" height=\"110\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                            <subviews>\n                                                <tableView focusRingType=\"none\" verticalHuggingPriority=\"750\" allowsExpansionToolTips=\"YES\" columnAutoresizingStyle=\"lastColumnOnly\" columnReordering=\"NO\" columnResizing=\"NO\" multipleSelection=\"NO\" autosaveColumns=\"NO\" typeSelect=\"NO\" rowSizeStyle=\"automatic\" viewBased=\"YES\" id=\"a4Q-Mp-SKh\">\n                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"258\" height=\"86\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                                    <size key=\"intercellSpacing\" width=\"3\" height=\"2\"/>\n                                                    <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                    <color key=\"gridColor\" name=\"gridColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                    <tableColumns>\n                                                        <tableColumn editable=\"NO\" width=\"217\" minWidth=\"40\" maxWidth=\"1000\" id=\"GgL-D9-Ium\">\n                                                            <tableHeaderCell key=\"headerCell\" lineBreakMode=\"truncatingTail\" borderStyle=\"border\" alignment=\"left\">\n                                                                <color key=\"textColor\" name=\"headerTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                                <color key=\"backgroundColor\" white=\"0.33333298560000002\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                                                            </tableHeaderCell>\n                                                            <textFieldCell key=\"dataCell\" lineBreakMode=\"truncatingTail\" selectable=\"YES\" editable=\"YES\" alignment=\"left\" title=\"Text Cell\" id=\"Jgu-cx-aQv\">\n                                                                <font key=\"font\" metaFont=\"system\"/>\n                                                                <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                                <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                            </textFieldCell>\n                                                            <tableColumnResizingMask key=\"resizingMask\" resizeWithTable=\"YES\"/>\n                                                            <connections>\n                                                                <binding destination=\"1St-Zh-cQw\" name=\"value\" keyPath=\"selection.windowTitles\" id=\"v4t-ds-led\"/>\n                                                            </connections>\n                                                        </tableColumn>\n                                                    </tableColumns>\n                                                    <connections>\n                                                        <binding destination=\"1St-Zh-cQw\" name=\"content\" keyPath=\"selection.windowTitles\" id=\"OfR-Mz-9nE\"/>\n                                                        <outlet property=\"delegate\" destination=\"-2\" id=\"hJN-cD-YmJ\"/>\n                                                    </connections>\n                                                </tableView>\n                                            </subviews>\n                                            <nil key=\"backgroundColor\"/>\n                                        </clipView>\n                                        <edgeInsets key=\"contentInsets\" left=\"0.0\" right=\"0.0\" top=\"0.0\" bottom=\"25\"/>\n                                        <scroller key=\"horizontalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"YES\" id=\"SVJ-m0-CnG\">\n                                            <rect key=\"frame\" x=\"1\" y=\"-14\" width=\"0.0\" height=\"15\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                        </scroller>\n                                        <scroller key=\"verticalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"NO\" id=\"RIO-9u-Kqs\">\n                                            <rect key=\"frame\" x=\"-14\" y=\"-7\" width=\"15\" height=\"0.0\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                        </scroller>\n                                    </scrollView>\n                                    <box boxType=\"custom\" borderType=\"none\" title=\"Box\" titlePosition=\"noTitle\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8B5-OE-QRo\">\n                                        <rect key=\"frame\" x=\"21\" y=\"71\" width=\"258\" height=\"25\"/>\n                                        <view key=\"contentView\" id=\"8kR-Vl-8St\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"258\" height=\"25\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                        </view>\n                                        <constraints>\n                                            <constraint firstAttribute=\"height\" constant=\"25\" id=\"wY4-wm-Qyg\"/>\n                                        </constraints>\n                                        <color key=\"fillColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                    </box>\n                                    <customView focusRingType=\"none\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bIY-NG-cWh\">\n                                        <rect key=\"frame\" x=\"20\" y=\"70\" width=\"260\" height=\"112\"/>\n                                        <subviews>\n                                            <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Gp2-JY-azv\">\n                                                <rect key=\"frame\" x=\"89\" y=\"48\" width=\"82\" height=\"16\"/>\n                                                <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"center\" title=\"All Windows\" id=\"q1g-cs-yOQ\">\n                                                    <font key=\"font\" metaFont=\"system\"/>\n                                                    <color key=\"textColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                    <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                </textFieldCell>\n                                            </textField>\n                                        </subviews>\n                                        <constraints>\n                                            <constraint firstItem=\"Gp2-JY-azv\" firstAttribute=\"centerX\" secondItem=\"bIY-NG-cWh\" secondAttribute=\"centerX\" id=\"MD7-WL-l2Z\"/>\n                                            <constraint firstItem=\"Gp2-JY-azv\" firstAttribute=\"centerY\" secondItem=\"bIY-NG-cWh\" secondAttribute=\"centerY\" id=\"mnS-u6-c4B\"/>\n                                        </constraints>\n                                        <connections>\n                                            <binding destination=\"1St-Zh-cQw\" name=\"hidden\" keyPath=\"selection.windowTitles.@count\" id=\"Hsy-RC-J6v\">\n                                                <dictionary key=\"options\">\n                                                    <string key=\"NSValueTransformerName\">AllWindowsHiddenTitlesCountTransformer</string>\n                                                </dictionary>\n                                            </binding>\n                                        </connections>\n                                    </customView>\n                                    <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"1nd-NB-kkJ\" userLabel=\"Add Window Title\">\n                                        <rect key=\"frame\" x=\"21\" y=\"69.5\" width=\"27.5\" height=\"29\"/>\n                                        <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSAddTemplate\" imagePosition=\"only\" alignment=\"center\" state=\"on\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"jIx-ts-Y1c\">\n                                            <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                                            <font key=\"font\" metaFont=\"system\"/>\n                                        </buttonCell>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"27\" id=\"1Fa-rq-rey\"/>\n                                        </constraints>\n                                        <connections>\n                                            <action selector=\"addWindowTitle:\" target=\"-2\" id=\"ZRi-ce-9P7\"/>\n                                        </connections>\n                                    </button>\n                                    <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"l9M-Jo-2BQ\" userLabel=\"Remove Window Title\">\n                                        <rect key=\"frame\" x=\"49\" y=\"74\" width=\"27.5\" height=\"20\"/>\n                                        <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSRemoveTemplate\" imagePosition=\"overlaps\" alignment=\"center\" state=\"on\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"TCY-v1-oeB\">\n                                            <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                                            <font key=\"font\" metaFont=\"system\"/>\n                                        </buttonCell>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"27\" id=\"dvo-EH-A9H\"/>\n                                        </constraints>\n                                        <connections>\n                                            <action selector=\"removeWindowTitle:\" target=\"-2\" id=\"VjH-qW-TC3\"/>\n                                        </connections>\n                                    </button>\n                                    <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" verticalCompressionResistancePriority=\"1000\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lU5-wk-Rkj\">\n                                        <rect key=\"frame\" x=\"18\" y=\"20\" width=\"264\" height=\"42\"/>\n                                        <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" title=\"Floating for the selected application will be restricted to the windows with titles matching the list above.\" id=\"0n3-tw-VMA\">\n                                            <font key=\"font\" metaFont=\"message\" size=\"11\"/>\n                                            <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                        </textFieldCell>\n                                    </textField>\n                                </subviews>\n                                <constraints>\n                                    <constraint firstItem=\"8B5-OE-QRo\" firstAttribute=\"bottom\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"bottom\" constant=\"-1\" id=\"1g2-Ek-1n7\"/>\n                                    <constraint firstItem=\"hb8-rU-qD8\" firstAttribute=\"top\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"top\" constant=\"20\" id=\"280-wu-d4C\"/>\n                                    <constraint firstItem=\"8B5-OE-QRo\" firstAttribute=\"trailing\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"trailing\" constant=\"-1\" id=\"8hD-7B-OM1\"/>\n                                    <constraint firstItem=\"lU5-wk-Rkj\" firstAttribute=\"leading\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"leading\" constant=\"20\" id=\"AZg-J5-FiU\"/>\n                                    <constraint firstItem=\"lU5-wk-Rkj\" firstAttribute=\"top\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"bottom\" constant=\"8\" id=\"Aa2-Ow-hXp\"/>\n                                    <constraint firstItem=\"1nd-NB-kkJ\" firstAttribute=\"leading\" secondItem=\"8B5-OE-QRo\" secondAttribute=\"leading\" id=\"Bzj-9U-eVh\"/>\n                                    <constraint firstItem=\"bIY-NG-cWh\" firstAttribute=\"trailing\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"trailing\" id=\"Ev7-jD-bHb\"/>\n                                    <constraint firstItem=\"bIY-NG-cWh\" firstAttribute=\"top\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"top\" id=\"GmH-bM-gE1\"/>\n                                    <constraint firstItem=\"hb8-rU-qD8\" firstAttribute=\"leading\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"leading\" constant=\"20\" id=\"Hbj-IE-K7q\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"lU5-wk-Rkj\" secondAttribute=\"trailing\" constant=\"20\" id=\"ICG-Lw-QZs\"/>\n                                    <constraint firstAttribute=\"trailing\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"trailing\" constant=\"20\" id=\"MZq-zK-ALV\"/>\n                                    <constraint firstItem=\"1nd-NB-kkJ\" firstAttribute=\"bottom\" secondItem=\"8B5-OE-QRo\" secondAttribute=\"bottom\" id=\"VlD-DU-rBe\"/>\n                                    <constraint firstItem=\"bIY-NG-cWh\" firstAttribute=\"leading\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"leading\" id=\"Woa-TT-tgc\"/>\n                                    <constraint firstItem=\"l9M-Jo-2BQ\" firstAttribute=\"bottom\" secondItem=\"8B5-OE-QRo\" secondAttribute=\"bottom\" id=\"eAw-xg-3KF\"/>\n                                    <constraint firstItem=\"l9M-Jo-2BQ\" firstAttribute=\"leading\" secondItem=\"1nd-NB-kkJ\" secondAttribute=\"trailing\" constant=\"1\" id=\"f9c-sN-I3k\"/>\n                                    <constraint firstAttribute=\"bottom\" secondItem=\"lU5-wk-Rkj\" secondAttribute=\"bottom\" constant=\"20\" id=\"fIJ-DI-j0D\"/>\n                                    <constraint firstItem=\"1nd-NB-kkJ\" firstAttribute=\"height\" secondItem=\"8B5-OE-QRo\" secondAttribute=\"height\" id=\"hCg-Wf-LkU\"/>\n                                    <constraint firstItem=\"8B5-OE-QRo\" firstAttribute=\"leading\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"leading\" constant=\"1\" id=\"jGk-Bc-top\"/>\n                                    <constraint firstItem=\"l9M-Jo-2BQ\" firstAttribute=\"height\" secondItem=\"8B5-OE-QRo\" secondAttribute=\"height\" id=\"jS1-eM-Iv1\"/>\n                                    <constraint firstItem=\"bIY-NG-cWh\" firstAttribute=\"bottom\" secondItem=\"hb8-rU-qD8\" secondAttribute=\"bottom\" id=\"kOq-Tz-B8t\"/>\n                                </constraints>\n                                <connections>\n                                    <binding destination=\"1St-Zh-cQw\" name=\"hidden\" keyPath=\"selectionIndex\" id=\"4VF-CB-HXz\">\n                                        <dictionary key=\"options\">\n                                            <string key=\"NSValueTransformerName\">NoApplicationSelectedTransformer</string>\n                                        </dictionary>\n                                    </binding>\n                                </connections>\n                            </customView>\n                            <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"c4Z-Xi-U9f\">\n                                <rect key=\"frame\" x=\"75\" y=\"93\" width=\"150\" height=\"16\"/>\n                                <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"No Application Selected\" id=\"JQ7-XU-Edg\">\n                                    <font key=\"font\" metaFont=\"system\"/>\n                                    <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                    <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                </textFieldCell>\n                                <connections>\n                                    <binding destination=\"1St-Zh-cQw\" name=\"hidden\" keyPath=\"selectionIndex\" id=\"Jom-By-RNh\">\n                                        <dictionary key=\"options\">\n                                            <string key=\"NSValueTransformerName\">ApplicationSelectedTransformer</string>\n                                        </dictionary>\n                                    </binding>\n                                </connections>\n                            </textField>\n                        </subviews>\n                        <constraints>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"bottom\" id=\"CC3-Ta-vZG\"/>\n                            <constraint firstItem=\"c4Z-Xi-U9f\" firstAttribute=\"centerY\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"centerY\" id=\"FSl-oH-0lT\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"trailing\" id=\"PN5-ac-A1O\"/>\n                            <constraint firstItem=\"ix5-Vi-yZw\" firstAttribute=\"leading\" secondItem=\"LKC-IK-MmH\" secondAttribute=\"leading\" id=\"WvF-ON-l0v\"/>\n                            <constraint firstItem=\"ix5-Vi-yZw\" firstAttribute=\"top\" secondItem=\"LKC-IK-MmH\" secondAttribute=\"top\" id=\"bZa-Cw-dOf\"/>\n                            <constraint firstItem=\"c4Z-Xi-U9f\" firstAttribute=\"centerX\" secondItem=\"ix5-Vi-yZw\" secondAttribute=\"centerX\" id=\"cW3-M6-JaK\"/>\n                        </constraints>\n                    </view>\n                </box>\n                <box hidden=\"YES\" boxType=\"custom\" borderType=\"none\" title=\"Box\" titlePosition=\"noTitle\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"KqU-3F-KwY\" userLabel=\"Add/Remove Background\">\n                    <rect key=\"frame\" x=\"31\" y=\"31\" width=\"178\" height=\"25\"/>\n                    <view key=\"contentView\" id=\"Olz-Y5-zJj\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"178\" height=\"25\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                    </view>\n                    <constraints>\n                        <constraint firstAttribute=\"height\" constant=\"25\" id=\"rBY-RH-o4k\"/>\n                    </constraints>\n                    <color key=\"fillColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                </box>\n            </subviews>\n            <constraints>\n                <constraint firstItem=\"cfp-8K-Cdt\" firstAttribute=\"height\" secondItem=\"KqU-3F-KwY\" secondAttribute=\"height\" id=\"6na-Qu-0VR\"/>\n                <constraint firstItem=\"Dvu-vy-f2R\" firstAttribute=\"leading\" secondItem=\"9ag-lu-Zd7\" secondAttribute=\"leading\" constant=\"4\" id=\"75T-ut-cBB\"/>\n                <constraint firstItem=\"ZH0-nz-Yiv\" firstAttribute=\"height\" secondItem=\"KqU-3F-KwY\" secondAttribute=\"height\" id=\"9va-we-5VG\"/>\n                <constraint firstItem=\"2vs-LF-ePe\" firstAttribute=\"leading\" secondItem=\"LsO-9M-hEw\" secondAttribute=\"leading\" constant=\"174\" id=\"B1L-DJ-iJP\"/>\n                <constraint firstItem=\"ZH0-nz-Yiv\" firstAttribute=\"leading\" secondItem=\"KqU-3F-KwY\" secondAttribute=\"leading\" id=\"B3f-2a-PRb\"/>\n                <constraint firstItem=\"Q7b-oi-oDI\" firstAttribute=\"leading\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"leading\" id=\"CVT-MO-a8f\"/>\n                <constraint firstItem=\"KqU-3F-KwY\" firstAttribute=\"bottom\" secondItem=\"U6O-Ka-pXq\" secondAttribute=\"bottom\" constant=\"-1\" id=\"Dhq-Tp-L2K\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"iHL-Zc-7k5\" secondAttribute=\"trailing\" constant=\"30\" id=\"EWD-Be-3wK\"/>\n                <constraint firstItem=\"Q7b-oi-oDI\" firstAttribute=\"top\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"bottom\" constant=\"4\" id=\"FDI-cj-WjQ\"/>\n                <constraint firstItem=\"2vs-LF-ePe\" firstAttribute=\"centerX\" secondItem=\"LsO-9M-hEw\" secondAttribute=\"centerX\" id=\"JaV-5o-ZS9\"/>\n                <constraint firstItem=\"Dvu-vy-f2R\" firstAttribute=\"top\" secondItem=\"9ag-lu-Zd7\" secondAttribute=\"bottom\" constant=\"8\" id=\"QIR-VF-jlk\"/>\n                <constraint firstItem=\"iHL-Zc-7k5\" firstAttribute=\"top\" secondItem=\"Dvu-vy-f2R\" secondAttribute=\"bottom\" constant=\"4\" id=\"RIb-zC-0MO\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"Q7b-oi-oDI\" secondAttribute=\"trailing\" constant=\"64\" id=\"Ra8-Cd-Qvy\"/>\n                <constraint firstItem=\"9ag-lu-Zd7\" firstAttribute=\"leading\" secondItem=\"LsO-9M-hEw\" secondAttribute=\"leading\" constant=\"30\" id=\"SEw-LG-2QH\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"2vs-LF-ePe\" secondAttribute=\"trailing\" constant=\"174\" id=\"UNG-SR-wqT\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"Mqz-bw-Kwm\" secondAttribute=\"trailing\" constant=\"362\" id=\"UWa-bY-ZoM\"/>\n                <constraint firstItem=\"Dvu-vy-f2R\" firstAttribute=\"trailing\" secondItem=\"9ag-lu-Zd7\" secondAttribute=\"trailing\" constant=\"4\" id=\"Vn7-XT-R4l\"/>\n                <constraint firstItem=\"9ag-lu-Zd7\" firstAttribute=\"top\" secondItem=\"2vs-LF-ePe\" secondAttribute=\"bottom\" constant=\"16\" id=\"agE-ed-IIM\"/>\n                <constraint firstItem=\"U6O-Ka-pXq\" firstAttribute=\"leading\" secondItem=\"LsO-9M-hEw\" secondAttribute=\"leading\" constant=\"30\" id=\"aoA-fJ-kna\"/>\n                <constraint firstItem=\"ZH0-nz-Yiv\" firstAttribute=\"bottom\" secondItem=\"KqU-3F-KwY\" secondAttribute=\"bottom\" id=\"dnE-Tl-n8w\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"9ag-lu-Zd7\" secondAttribute=\"trailing\" constant=\"30\" id=\"dr3-F4-Vtr\"/>\n                <constraint firstAttribute=\"bottom\" secondItem=\"U6O-Ka-pXq\" secondAttribute=\"bottom\" constant=\"30\" id=\"fQN-cG-rch\"/>\n                <constraint firstItem=\"U6O-Ka-pXq\" firstAttribute=\"top\" secondItem=\"Dvu-vy-f2R\" secondAttribute=\"bottom\" constant=\"8\" id=\"jTW-ge-qN6\"/>\n                <constraint firstItem=\"9hq-U6-t9B\" firstAttribute=\"centerY\" secondItem=\"Mqz-bw-Kwm\" secondAttribute=\"centerY\" id=\"qde-oI-Gox\"/>\n                <constraint firstItem=\"sWs-Th-001\" firstAttribute=\"leading\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"trailing\" constant=\"8\" id=\"sWs-Cn-001\"/>\n                <constraint firstItem=\"sWs-Th-001\" firstAttribute=\"centerY\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"centerY\" id=\"sWs-Cn-002\"/>\n                <constraint firstItem=\"sWs-Th-004\" firstAttribute=\"leading\" secondItem=\"sWs-Th-001\" secondAttribute=\"trailing\" constant=\"4\" id=\"sWs-Cn-003\"/>\n                <constraint firstItem=\"sWs-Th-004\" firstAttribute=\"centerY\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"centerY\" id=\"sWs-Cn-004\"/>\n                <constraint firstItem=\"sWs-Th-009\" firstAttribute=\"leading\" secondItem=\"sWs-Th-004\" secondAttribute=\"trailing\" constant=\"0\" id=\"sWs-Cn-005\"/>\n                <constraint firstItem=\"sWs-Th-009\" firstAttribute=\"centerY\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"centerY\" id=\"sWs-Cn-006\"/>\n                <constraint firstItem=\"sWs-Th-013\" firstAttribute=\"leading\" secondItem=\"sWs-Th-009\" secondAttribute=\"trailing\" constant=\"2\" id=\"sWs-Cn-007\"/>\n                <constraint firstItem=\"sWs-Th-013\" firstAttribute=\"centerY\" secondItem=\"9hq-U6-t9B\" secondAttribute=\"centerY\" id=\"sWs-Cn-008\"/>\n                <constraint firstItem=\"cfp-8K-Cdt\" firstAttribute=\"leading\" secondItem=\"ZH0-nz-Yiv\" secondAttribute=\"trailing\" constant=\"1\" id=\"rvY-DC-e48\"/>\n                <constraint firstItem=\"Mqz-bw-Kwm\" firstAttribute=\"top\" secondItem=\"LsO-9M-hEw\" secondAttribute=\"top\" constant=\"30\" id=\"saa-p3-iK6\"/>\n                <constraint firstItem=\"KqU-3F-KwY\" firstAttribute=\"leading\" secondItem=\"U6O-Ka-pXq\" secondAttribute=\"leading\" constant=\"1\" id=\"tZl-oJ-mAu\"/>\n                <constraint firstItem=\"cfp-8K-Cdt\" firstAttribute=\"bottom\" secondItem=\"KqU-3F-KwY\" secondAttribute=\"bottom\" id=\"tfX-zU-XXr\"/>\n                <constraint firstItem=\"KqU-3F-KwY\" firstAttribute=\"trailing\" secondItem=\"U6O-Ka-pXq\" secondAttribute=\"trailing\" constant=\"-1\" id=\"uO1-Kx-LGb\"/>\n                <constraint firstItem=\"iHL-Zc-7k5\" firstAttribute=\"leading\" secondItem=\"U6O-Ka-pXq\" secondAttribute=\"trailing\" priority=\"750\" constant=\"8\" id=\"w6T-H7-UiM\"/>\n                <constraint firstItem=\"9hq-U6-t9B\" firstAttribute=\"leading\" secondItem=\"Mqz-bw-Kwm\" secondAttribute=\"trailing\" constant=\"8\" id=\"wai-Xo-qBA\"/>\n                <constraint firstAttribute=\"bottom\" secondItem=\"iHL-Zc-7k5\" secondAttribute=\"bottom\" constant=\"30\" id=\"yF3-VW-Xkt\"/>\n                <constraint firstItem=\"2vs-LF-ePe\" firstAttribute=\"top\" secondItem=\"Q7b-oi-oDI\" secondAttribute=\"bottom\" constant=\"30\" id=\"zpH-aZ-dri\"/>\n            </constraints>\n            <point key=\"canvasLocation\" x=\"561\" y=\"227.5\"/>\n        </customView>\n        <userDefaultsController id=\"XhK-Tn-zrU\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"N2H-cZ-f1m\"/>\n        <arrayController preservesSelection=\"NO\" id=\"1St-Zh-cQw\" userLabel=\"Floating Bundles Array Controller\"/>\n    </objects>\n    <resources>\n        <image name=\"NSAddTemplate\" width=\"18\" height=\"17\"/>\n        <image name=\"NSRemoveTemplate\" width=\"18\" height=\"5\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/GeneralPreferencesViewController.swift",
    "content": "//\n//  GeneralPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport Silica\n\nclass GeneralPreferencesViewController: NSViewController {}\n"
  },
  {
    "path": "Amethyst/Preferences/GeneralPreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"17701\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"17701\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"GeneralPreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"view\" destination=\"LsO-9M-hEw\" id=\"7dy-Su-MSH\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customView id=\"LsO-9M-hEw\" userLabel=\"General Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"548\" height=\"560\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"luh-Gm-ZV0\">\n                    <rect key=\"frame\" x=\"79\" y=\"321\" width=\"109\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"Window Margins:\" id=\"LnD-6G-RSL\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tXC-cK-Yql\">\n                    <rect key=\"frame\" x=\"193\" y=\"320\" width=\"265\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Enabled\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"59W-1k-G1j\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-margins\" id=\"e8M-Nk-Gy6\"/>\n                    </connections>\n                </button>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Ric-uO-1Cb\">\n                    <rect key=\"frame\" x=\"196\" y=\"292\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"Qi6-Bv-Yh1\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"3\" id=\"gi8-52-60I\">\n                            <real key=\"minimum\" value=\"0.0\"/>\n                        </numberFormatter>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <connections>\n                            <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-margin-size\" id=\"Jf3-xD-At6\"/>\n                        </connections>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-margin-size\" id=\"TLY-xG-dxJ\"/>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.window-margins\" id=\"ViG-ED-51L\"/>\n                    </connections>\n                </textField>\n                <button verticalHuggingPriority=\"750\" id=\"aFO-Xs-m2t\">\n                    <rect key=\"frame\" x=\"194\" y=\"268\" width=\"163\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Smart Window Margins\" bezelStyle=\"regularSquare\" imagePosition=\"left\" inset=\"2\" id=\"ZRJ-ZP-g2F\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.smart-window-margins\" id=\"zRQ-9W-cK5\"/>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.window-margins\" id=\"OOA-MA-LCS\"/>\n                    </connections>\n                </button>\n                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3OU-K6-S5G\">\n                    <rect key=\"frame\" x=\"193\" y=\"225\" width=\"336\" height=\"42\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" title=\"When enabled, margins will not be applied on fullscreen layout or when there is only one window\" id=\"4nQ-Bx-cFB\">\n                        <font key=\"font\" metaFont=\"label\" size=\"11\"/>\n                        <color key=\"textColor\" name=\"secondaryLabelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pwW-SE-wmY\">\n                    <rect key=\"frame\" x=\"251\" y=\"295\" width=\"19\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"px\" id=\"Qfs-D0-k4Z\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"VsK-s4-pep\">\n                    <rect key=\"frame\" x=\"234\" y=\"289\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" minValue=\"1\" maxValue=\"100\" doubleValue=\"1\" id=\"kSN-EK-HUy\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.window-margin-size\" id=\"3Yq-yI-KEa\"/>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"enabled\" keyPath=\"values.window-margins\" id=\"TKt-Pl-ye5\"/>\n                    </connections>\n                </stepper>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Va2-yZ-Rd5\">\n                    <rect key=\"frame\" x=\"195\" y=\"346\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"h2u-LR-IWG\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"3\" id=\"U0n-fW-sVR\">\n                            <real key=\"minimum\" value=\"0.0\"/>\n                        </numberFormatter>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-minimum-height\" id=\"Gc5-YY-n9Q\"/>\n                    </connections>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"DW3-mi-ftN\">\n                    <rect key=\"frame\" x=\"250\" y=\"349\" width=\"19\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"px\" id=\"hSf-ej-InP\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"AUp-dd-yza\">\n                    <rect key=\"frame\" x=\"233\" y=\"343\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"10\" maxValue=\"5000\" id=\"8qr-vt-jVF\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.window-minimum-height\" id=\"9xi-qU-Dp5\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"1nR-DJ-LYU\">\n                    <rect key=\"frame\" x=\"29\" y=\"348\" width=\"159\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"Window Minimum Height:\" id=\"mfB-fg-Gyl\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gyf-rg-uCJ\">\n                    <rect key=\"frame\" x=\"195\" y=\"372\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"w0U-n3-qzM\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"3\" id=\"R7z-Le-F75\">\n                            <real key=\"minimum\" value=\"0.0\"/>\n                        </numberFormatter>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-minimum-width\" id=\"kSC-EK-Sgz\"/>\n                    </connections>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"qmJ-gR-Sn1\">\n                    <rect key=\"frame\" x=\"250\" y=\"375\" width=\"19\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"px\" id=\"vw6-0c-vUA\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zRv-u5-qob\">\n                    <rect key=\"frame\" x=\"233\" y=\"369\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"10\" maxValue=\"5000\" id=\"5XK-xo-aFK\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.window-minimum-width\" id=\"IQ2-rQ-gza\"/>\n                    </connections>\n                </stepper>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"MJk-MS-1xp\">\n                    <rect key=\"frame\" x=\"234\" y=\"393\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" maxValue=\"30\" id=\"y24-P1-XzF\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.window-max-count\" id=\"bXm-k8-TOS\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"JVK-ci-h9a\">\n                    <rect key=\"frame\" x=\"33\" y=\"374\" width=\"155\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"Window Minimum Width:\" id=\"0ju-6y-OxU\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"dtw-c2-sHK\">\n                    <rect key=\"frame\" x=\"33\" y=\"399\" width=\"155\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"Maximum Window Count:\" id=\"tjT-rh-6Es\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aBS-2b-SAH\">\n                    <rect key=\"frame\" x=\"264\" y=\"211\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" placeholderString=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"WdP-se-3sK\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" usesGroupingSeparator=\"NO\" groupingSize=\"0\" minimumIntegerDigits=\"0\" maximumIntegerDigits=\"42\" id=\"NeO-ui-ZjP\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.screen-padding-top\" id=\"cDE-bl-UiG\"/>\n                    </connections>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZIo-fZ-aA2\">\n                    <rect key=\"frame\" x=\"302\" y=\"209\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"5\" maxValue=\"5000\" id=\"JaW-te-IlJ\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.screen-padding-top\" id=\"zGY-a5-ecb\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"yRU-2t-8Ss\">\n                    <rect key=\"frame\" x=\"319\" y=\"214\" width=\"23\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" title=\"px\" id=\"Cim-aU-dOb\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"G9Q-nh-ohh\">\n                    <rect key=\"frame\" x=\"264\" y=\"157\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" placeholderString=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"V9T-4b-d2u\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" usesGroupingSeparator=\"NO\" groupingSize=\"0\" minimumIntegerDigits=\"0\" maximumIntegerDigits=\"42\" id=\"MC2-Tt-9Ep\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.screen-padding-bottom\" id=\"QEe-Dd-Tvh\"/>\n                    </connections>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"XnN-yy-z4d\">\n                    <rect key=\"frame\" x=\"302\" y=\"155\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"5\" maxValue=\"5000\" id=\"A8C-jQ-r9M\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.screen-padding-bottom\" id=\"xiJ-qv-kz4\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"tR7-ZK-BJ1\">\n                    <rect key=\"frame\" x=\"319\" y=\"160\" width=\"23\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" title=\"px\" id=\"POe-gx-hyp\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"X5C-59-n7f\">\n                    <rect key=\"frame\" x=\"336\" y=\"183\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" placeholderString=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"dIm-1r-QMy\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" usesGroupingSeparator=\"NO\" groupingSize=\"0\" minimumIntegerDigits=\"0\" maximumIntegerDigits=\"42\" id=\"60d-hq-KK6\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.screen-padding-right\" id=\"GPK-6F-gwS\"/>\n                    </connections>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"zhb-4B-fZW\">\n                    <rect key=\"frame\" x=\"374\" y=\"181\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"5\" maxValue=\"5000\" id=\"slo-1Z-5AM\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.screen-padding-right\" id=\"Py1-fT-dGS\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"BZo-E0-dkW\">\n                    <rect key=\"frame\" x=\"391\" y=\"186\" width=\"23\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" title=\"px\" id=\"Lpd-Mr-7br\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"CJB-Px-reM\">\n                    <rect key=\"frame\" x=\"194\" y=\"183\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" placeholderString=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"KNK-Rf-z97\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" usesGroupingSeparator=\"NO\" groupingSize=\"0\" minimumIntegerDigits=\"0\" maximumIntegerDigits=\"42\" id=\"cJj-vJ-pVq\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.screen-padding-left\" id=\"Row-jY-Esk\"/>\n                    </connections>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gA8-3I-b6U\">\n                    <rect key=\"frame\" x=\"232\" y=\"181\" width=\"19\" height=\"27\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" increment=\"5\" maxValue=\"5000\" id=\"zqe-Hg-SVJ\"/>\n                    <connections>\n                        <binding destination=\"N2H-cZ-f1m\" name=\"value\" keyPath=\"values.screen-padding-left\" id=\"kCA-Wa-uVX\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"9LL-fQ-X4s\">\n                    <rect key=\"frame\" x=\"249\" y=\"186\" width=\"23\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" title=\"px\" id=\"HYL-Bb-2dr\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"TON-41-ZcD\">\n                    <rect key=\"frame\" x=\"85\" y=\"188\" width=\"103\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" alignment=\"justified\" title=\"Screen Padding:\" id=\"HDC-ka-f0i\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <box verticalHuggingPriority=\"750\" fixedFrame=\"YES\" boxType=\"separator\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"vr5-JE-vwO\">\n                    <rect key=\"frame\" x=\"172\" y=\"124\" width=\"200\" height=\"5\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                </box>\n                <box verticalHuggingPriority=\"750\" fixedFrame=\"YES\" boxType=\"separator\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sO2-ls-SUu\">\n                    <rect key=\"frame\" x=\"172\" y=\"422\" width=\"200\" height=\"5\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                </box>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Jbq-77-1YW\">\n                    <rect key=\"frame\" x=\"192\" y=\"79\" width=\"249\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Display layout when changing layouts\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"UVr-KG-wFB\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.enables-layout-hud\" id=\"fVj-ab-n2P\"/>\n                    </connections>\n                </button>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ej5-FV-H6Q\">\n                    <rect key=\"frame\" x=\"192\" y=\"59\" width=\"255\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Display layout when changing spaces\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"o6e-e0-t64\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.enables-layout-hud-on-space-change\" id=\"1rV-d7-6jx\"/>\n                    </connections>\n                </button>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wnd-cnt-hud\">\n                    <rect key=\"frame\" x=\"192\" y=\"39\" width=\"320\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Notify when changing maximum window count\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"wnd-cnt-hud-cell\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.enables-window-count-hud\" id=\"wnd-cnt-hud-bind\"/>\n                    </connections>\n                </button>\n\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"MKd-ZG-j26\">\n                    <rect key=\"frame\" x=\"28\" y=\"79\" width=\"160\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"Heads Up Display:\" id=\"dFS-Je-lML\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"pGc-zn-uC7\">\n                    <rect key=\"frame\" x=\"192\" y=\"514\" width=\"133\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Ignore menu bars\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"LKn-n5-1ay\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.ignore-menu-bar\" id=\"xsZ-ve-euv\"/>\n                    </connections>\n                </button>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Xay-gW-zGw\">\n                    <rect key=\"frame\" x=\"192\" y=\"494\" width=\"223\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Send new windows to main pane\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"stG-cM-1bg\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.new-windows-to-main\" id=\"c0g-Ta-qOE\"/>\n                    </connections>\n                </button>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"hed-5T-mt5\">\n                    <rect key=\"frame\" x=\"192\" y=\"474\" width=\"265\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Follow thrown windows between spaces\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"1GI-LA-8ET\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.follow-space-thrown-windows\" id=\"rNs-ia-fel\"/>\n                    </connections>\n                </button>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"eDY-rg-FIm\">\n                    <rect key=\"frame\" x=\"132\" y=\"513\" width=\"56\" height=\"17\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"General:\" id=\"oHM-Bk-m8H\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"q7S-uK-u6Y\">\n                    <rect key=\"frame\" x=\"196\" y=\"396\" width=\"40\" height=\"22\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" title=\"0\" drawsBackground=\"YES\" usesSingleLineMode=\"YES\" id=\"YGR-J5-VCh\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"3\" id=\"Z5V-Io-vQx\">\n                            <real key=\"minimum\" value=\"0.0\"/>\n                        </numberFormatter>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.window-max-count\" id=\"WIW-VS-GfJ\"/>\n                    </connections>\n                </textField>\n                <button verticalHuggingPriority=\"750\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"IK4-QB-Sbo\">\n                    <rect key=\"frame\" x=\"192\" y=\"134\" width=\"280\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Disable screen padding on built-in display\" bezelStyle=\"regularSquare\" imagePosition=\"left\" inset=\"2\" id=\"9Vr-QQ-tmB\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.disable-padding-on-builtin-display\" id=\"LEd-HG-IyE\"/>\n                    </connections>\n                </button>\n                <button fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"W2T-GM-Abn\">\n                    <rect key=\"frame\" x=\"192\" y=\"452\" width=\"265\" height=\"18\"/>\n                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Hide menu bar icon\" bezelStyle=\"regularSquare\" imagePosition=\"left\" inset=\"2\" id=\"Tdh-J4-2ut\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"XhK-Tn-zrU\" name=\"value\" keyPath=\"values.hide-menu-bar-icon\" id=\"xeg-d8-MWC\"/>\n                    </connections>\n                </button>\n            </subviews>\n            <point key=\"canvasLocation\" x=\"561\" y=\"274\"/>\n        </customView>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <userDefaultsController id=\"XhK-Tn-zrU\"/>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"N2H-cZ-f1m\"/>\n    </objects>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/LayoutsPreferencesViewController.swift",
    "content": "//\n//  LayoutsPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/1/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\n\nclass LayoutsPreferencesViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {\n    private var layoutKeys: [String] = []\n\n    @IBOutlet var layoutsTableView: NSTableView?\n    @IBOutlet weak var openCustomLayoutsFolderButton: NSButton?\n    @IBOutlet weak var relaunchButton: NSButton?\n\n    override func awakeFromNib() {\n        super.awakeFromNib()\n\n        layoutsTableView?.dataSource = self\n        layoutsTableView?.delegate = self\n        layoutsTableView?.registerForDraggedTypes([.string])\n    }\n\n    override func viewWillAppear() {\n        super.viewWillAppear()\n\n        layoutKeys = UserConfiguration.shared.layoutKeys()\n\n        layoutsTableView?.reloadData()\n    }\n\n    @IBAction func addLayout(_ sender: NSButton) {\n        let layoutMenu = NSMenu(title: \"\")\n\n        for (layoutKey, layoutName) in LayoutType<AXWindow>.availableLayoutStrings() {\n            let menuItem = NSMenuItem(title: layoutName, action: #selector(addLayoutString(_:)), keyEquivalent: \"\")\n            menuItem.representedObject = layoutKey\n            menuItem.target = self\n            menuItem.action = #selector(addLayoutString(_:))\n\n            layoutMenu.addItem(menuItem)\n        }\n\n        let frame = sender.frame\n        let menuOrigin = sender.superview!.convert(NSPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height + 40), to: nil)\n\n        let event = NSEvent.mouseEvent(\n            with: NSEvent.EventType.leftMouseDown,\n            location: menuOrigin,\n            modifierFlags: [],\n            timestamp: 0,\n            windowNumber: sender.window!.windowNumber,\n            context: sender.window!.graphicsContext,\n            eventNumber: 0,\n            clickCount: 1,\n            pressure: 1\n        )\n\n        NSMenu.popUpContextMenu(layoutMenu, with: event!, for: sender)\n    }\n\n    @IBAction func addLayoutString(_ sender: NSMenuItem) {\n        guard let layoutKey: String = sender.representedObject as? String else { return }\n\n        var layoutKeys = self.layoutKeys\n        layoutKeys.append(layoutKey)\n        self.layoutKeys = layoutKeys\n\n        UserConfiguration.shared.setLayoutKeys(self.layoutKeys)\n\n        relaunchButton?.isEnabled = true\n        layoutsTableView?.reloadData()\n    }\n\n    @IBAction func removeLayout(_ sender: AnyObject) {\n        guard let selectedRow = layoutsTableView?.selectedRow, selectedRow < self.layoutKeys.count, selectedRow != NSTableView.noRowSelectedIndex else { return }\n\n        layoutKeys.remove(at: selectedRow)\n\n        UserConfiguration.shared.setLayoutKeys(layoutKeys)\n\n        relaunchButton?.isEnabled = true\n        layoutsTableView?.reloadData()\n    }\n\n    func numberOfRows(in tableView: NSTableView) -> Int {\n        return layoutKeys.count\n    }\n\n    func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {\n        guard row > -1 else {\n            return nil\n        }\n\n        return LayoutType<AXWindow>.layoutNameForKey(layoutKeys[row])\n    }\n\n    @IBAction func relaunch(_ sender: AnyObject) {\n        AppManager.relaunch()\n    }\n\n    @IBAction func openCustomLayoutsFolder(_ sender: AnyObject) {\n        do {\n            let layoutsDirectory = try FileManager.default.layoutsDirectory()\n            NSWorkspace.shared.open(layoutsDirectory)\n        } catch {\n            // Handle error - could show an alert or log the error\n            log.error(\"Failed to open layouts directory: \\(error)\")\n        }\n    }\n\n    func tableView(_ tableView: NSTableView, acceptDrop info: NSDraggingInfo, row: Int, dropOperation: NSTableView.DropOperation) -> Bool {\n        if let dragData = info.draggingPasteboard.data(forType: .string),\n            let rowString = String(bytes: dragData, encoding: .utf8),\n            let oldRow = Int(rowString) {\n\n            layoutKeys.move(from: oldRow, to: oldRow < row ? row-1 : row)\n            UserConfiguration.shared.setLayoutKeys(self.layoutKeys)\n            layoutsTableView?.reloadData()\n            return true\n        }\n        return false\n    }\n\n    func tableView(_ tableView: NSTableView, validateDrop info: NSDraggingInfo, proposedRow row: Int, proposedDropOperation dropOperation: NSTableView.DropOperation) -> NSDragOperation {\n        if dropOperation == .above {\n            return .move\n        }\n        return []\n\n    }\n\n    func tableView(_ tableView: NSTableView, pasteboardWriterForRow row: Int) -> NSPasteboardWriting? {\n        let item = NSPasteboardItem()\n        item.setString(String(row), forType: .string)\n        return item\n    }\n}\n\nprivate extension Array {\n    mutating func move(from oldIndex: Index, to newIndex: Index) {\n        if oldIndex == newIndex { return }\n        if abs(newIndex - oldIndex) == 1 { return self.swapAt(oldIndex, newIndex) }\n        self.insert(self.remove(at: oldIndex), at: newIndex)\n    }\n}\n"
  },
  {
    "path": "Amethyst/Preferences/LayoutsPreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"18122\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"18122\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"LayoutsPreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"layoutsTableView\" destination=\"9o5-6W-pBB\" id=\"kkq-fe-TRt\"/>\n                <outlet property=\"openCustomLayoutsFolderButton\" destination=\"bR4-fH-9Xy\" id=\"nPz-Ql-8Rf\"/>\n                <outlet property=\"relaunchButton\" destination=\"aO3-09-OQh\" id=\"xn1-ST-HKf\"/>\n                <outlet property=\"view\" destination=\"qtZ-jm-AmG\" id=\"qMb-OA-4eh\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customView id=\"qtZ-jm-AmG\" userLabel=\"General Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"510\" height=\"474\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <scrollView focusRingType=\"none\" autohidesScrollers=\"YES\" horizontalLineScroll=\"19\" horizontalPageScroll=\"10\" verticalLineScroll=\"19\" verticalPageScroll=\"10\" usesPredominantAxisScrolling=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"TUI-Vc-F84\">\n                    <rect key=\"frame\" x=\"30\" y=\"99\" width=\"450\" height=\"244\"/>\n                    <clipView key=\"contentView\" focusRingType=\"none\" id=\"epl-9R-f3U\">\n                        <rect key=\"frame\" x=\"1\" y=\"1\" width=\"448\" height=\"242\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <tableView focusRingType=\"none\" verticalHuggingPriority=\"750\" allowsExpansionToolTips=\"YES\" columnAutoresizingStyle=\"lastColumnOnly\" columnSelection=\"YES\" multipleSelection=\"NO\" autosaveColumns=\"NO\" id=\"9o5-6W-pBB\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"448\" height=\"242\"/>\n                                <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                <size key=\"intercellSpacing\" width=\"3\" height=\"2\"/>\n                                <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                <color key=\"gridColor\" name=\"gridColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                <tableColumns>\n                                    <tableColumn editable=\"NO\" width=\"407\" minWidth=\"40\" maxWidth=\"1000\" id=\"iX3-vh-qVg\">\n                                        <tableHeaderCell key=\"headerCell\" lineBreakMode=\"truncatingTail\" borderStyle=\"border\" alignment=\"left\">\n                                            <color key=\"textColor\" name=\"headerTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"backgroundColor\" white=\"0.33333298560000002\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                                        </tableHeaderCell>\n                                        <textFieldCell key=\"dataCell\" lineBreakMode=\"truncatingTail\" selectable=\"YES\" editable=\"YES\" alignment=\"left\" title=\"Text Cell\" id=\"GHa-qO-WJ2\">\n                                            <font key=\"font\" metaFont=\"system\"/>\n                                            <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                        </textFieldCell>\n                                        <tableColumnResizingMask key=\"resizingMask\" resizeWithTable=\"YES\"/>\n                                    </tableColumn>\n                                </tableColumns>\n                            </tableView>\n                        </subviews>\n                    </clipView>\n                    <scroller key=\"horizontalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"YES\" id=\"0fE-FN-6Yl\">\n                        <rect key=\"frame\" x=\"1\" y=\"326\" width=\"448\" height=\"16\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </scroller>\n                    <scroller key=\"verticalScroller\" hidden=\"YES\" wantsLayer=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"NO\" id=\"WjR-a1-uJg\">\n                        <rect key=\"frame\" x=\"-14\" y=\"-7\" width=\"15\" height=\"0.0\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </scroller>\n                </scrollView>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"gMn-n1-4Ff\">\n                    <rect key=\"frame\" x=\"31\" y=\"78\" width=\"27\" height=\"23\"/>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"27\" id=\"Nqb-jp-o4F\"/>\n                    </constraints>\n                    <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSAddTemplate\" imagePosition=\"overlaps\" alignment=\"center\" state=\"on\" borderStyle=\"border\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"Vxf-d2-4iT\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <action selector=\"addLayout:\" target=\"-2\" id=\"JKi-wK-bKV\"/>\n                    </connections>\n                </button>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZN0-93-er7\">\n                    <rect key=\"frame\" x=\"58\" y=\"78\" width=\"27\" height=\"23\"/>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"27\" id=\"f5E-ZR-Cjg\"/>\n                    </constraints>\n                    <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" image=\"NSRemoveTemplate\" imagePosition=\"overlaps\" alignment=\"center\" state=\"on\" borderStyle=\"border\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"S90-L3-spC\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <action selector=\"removeLayout:\" target=\"-2\" id=\"4NO-n9-KVI\"/>\n                    </connections>\n                </button>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"u8n-2E-r2x\">\n                    <rect key=\"frame\" x=\"85\" y=\"78\" width=\"394\" height=\"23\"/>\n                    <constraints>\n                        <constraint firstAttribute=\"height\" constant=\"21\" id=\"dxv-S2-d20\"/>\n                    </constraints>\n                    <buttonCell key=\"cell\" type=\"smallSquare\" bezelStyle=\"smallSquare\" imagePosition=\"overlaps\" alignment=\"center\" refusesFirstResponder=\"YES\" state=\"on\" borderStyle=\"border\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"cg0-eO-Uvf\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                </button>\n                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ZlK-na-Nuc\">\n                    <rect key=\"frame\" x=\"31\" y=\"60\" width=\"452\" height=\"14\"/>\n                    <textFieldCell key=\"cell\" sendsActionOnEndEditing=\"YES\" title=\"You must relaunch for changes to take effect\" id=\"AwT-2l-YBR\">\n                        <font key=\"font\" metaFont=\"label\" size=\"11\"/>\n                        <color key=\"textColor\" name=\"secondaryLabelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"EqO-8K-pe7\">\n                    <rect key=\"frame\" x=\"154\" y=\"427\" width=\"182\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Restore layouts on launch\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"Olr-n2-FUF\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"fHp-J5-akw\" name=\"value\" keyPath=\"values.restore-layouts-on-launch\" id=\"kQy-kY-JXe\"/>\n                    </connections>\n                </button>\n                <box verticalHuggingPriority=\"750\" boxType=\"separator\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Thm-8g-Ol9\">\n                    <rect key=\"frame\" x=\"155\" y=\"371\" width=\"200\" height=\"5\"/>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"200\" id=\"EC8-tw-V5W\"/>\n                    </constraints>\n                </box>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"yGP-qi-0QH\">\n                    <rect key=\"frame\" x=\"95\" y=\"428\" width=\"55\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"General:\" id=\"oS2-EX-GQs\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <textField verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"JHN-38-Vcl\" userLabel=\"step size field\">\n                    <rect key=\"frame\" x=\"156\" y=\"402\" width=\"32\" height=\"21\"/>\n                    <constraints>\n                        <constraint firstAttribute=\"width\" constant=\"32\" id=\"iZa-kT-ty9\"/>\n                    </constraints>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" state=\"on\" borderStyle=\"bezel\" alignment=\"right\" title=\"5\" placeholderString=\"5\" drawsBackground=\"YES\" id=\"ens-ER-ZXO\">\n                        <numberFormatter key=\"formatter\" formatterBehavior=\"default10_4\" numberStyle=\"decimal\" minimumIntegerDigits=\"1\" maximumIntegerDigits=\"2000000000\" maximumFractionDigits=\"3\" id=\"U1q-fY-TPr\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"textColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                    <connections>\n                        <binding destination=\"fHp-J5-akw\" name=\"value\" keyPath=\"values.window-resize-step\" id=\"SuP-5p-6LT\"/>\n                    </connections>\n                </textField>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Iqr-xS-F3Y\">\n                    <rect key=\"frame\" x=\"2\" y=\"404\" width=\"148\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"Window Resize Interval:\" id=\"2yM-aM-aV7\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <stepper horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"jyc-WA-HVd\">\n                    <rect key=\"frame\" x=\"186\" y=\"398\" width=\"19\" height=\"28\"/>\n                    <stepperCell key=\"cell\" continuous=\"YES\" alignment=\"left\" minValue=\"1\" maxValue=\"100\" doubleValue=\"1\" id=\"DQ7-2X-I9c\"/>\n                    <connections>\n                        <binding destination=\"fHp-J5-akw\" name=\"value\" keyPath=\"values.window-resize-step\" id=\"3Vt-78-kkT\"/>\n                    </connections>\n                </stepper>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"nHi-Ex-f22\">\n                    <rect key=\"frame\" x=\"202\" y=\"404\" width=\"16\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" title=\"%\" id=\"Ryz-ML-0Fd\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"labelColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"bR4-fH-9Xy\">\n                    <rect key=\"frame\" x=\"155\" y=\"23\" width=\"173\" height=\"32\"/>\n                    <buttonCell key=\"cell\" type=\"push\" title=\"Open Custom Layouts Folder\" bezelStyle=\"rounded\" alignment=\"center\" borderStyle=\"border\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"mK8-Rt-3Ln\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <connections>\n                            <action selector=\"openCustomLayoutsFolder:\" target=\"-2\" id=\"gP9-Zh-4Xt\"/>\n                        </connections>\n                    </buttonCell>\n                </button>\n                <button verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"aO3-09-OQh\">\n                    <rect key=\"frame\" x=\"336\" y=\"23\" width=\"151\" height=\"32\"/>\n                    <buttonCell key=\"cell\" type=\"push\" title=\"Relaunch Amethyst\" bezelStyle=\"rounded\" alignment=\"center\" enabled=\"NO\" borderStyle=\"border\" imageScaling=\"proportionallyDown\" inset=\"2\" id=\"L4e-Ct-jBn\">\n                        <behavior key=\"behavior\" pushIn=\"YES\" lightByBackground=\"YES\" lightByGray=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <connections>\n                            <action selector=\"relaunch:\" target=\"-2\" id=\"UNl-6Q-gGz\"/>\n                        </connections>\n                    </buttonCell>\n                </button>\n            </subviews>\n            <constraints>\n                <constraint firstItem=\"JHN-38-Vcl\" firstAttribute=\"leading\" secondItem=\"Iqr-xS-F3Y\" secondAttribute=\"trailing\" constant=\"8\" id=\"0dK-0w-9Oa\"/>\n                <constraint firstItem=\"u8n-2E-r2x\" firstAttribute=\"top\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"bottom\" constant=\"-1\" id=\"0e4-gV-yjc\"/>\n                <constraint firstAttribute=\"bottom\" secondItem=\"aO3-09-OQh\" secondAttribute=\"bottom\" constant=\"30\" id=\"0y9-aP-br8\"/>\n                <constraint firstAttribute=\"bottom\" secondItem=\"bR4-fH-9Xy\" secondAttribute=\"bottom\" constant=\"30\" id=\"1B2-cT-dX9\"/>\n                <constraint firstItem=\"nHi-Ex-f22\" firstAttribute=\"centerY\" secondItem=\"jyc-WA-HVd\" secondAttribute=\"centerY\" id=\"7Ie-Oz-lWs\"/>\n                <constraint firstItem=\"ZlK-na-Nuc\" firstAttribute=\"top\" secondItem=\"u8n-2E-r2x\" secondAttribute=\"bottom\" constant=\"5\" id=\"7Sr-BD-203\"/>\n                <constraint firstItem=\"yGP-qi-0QH\" firstAttribute=\"top\" secondItem=\"qtZ-jm-AmG\" secondAttribute=\"top\" constant=\"30\" id=\"88S-d8-abc\"/>\n                <constraint firstItem=\"gMn-n1-4Ff\" firstAttribute=\"height\" secondItem=\"u8n-2E-r2x\" secondAttribute=\"height\" id=\"92m-mx-3EP\"/>\n                <constraint firstItem=\"ZlK-na-Nuc\" firstAttribute=\"leading\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"leading\" constant=\"3\" id=\"CxX-jz-5DU\"/>\n                <constraint firstItem=\"gMn-n1-4Ff\" firstAttribute=\"leading\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"leading\" constant=\"1\" id=\"Jko-Xd-KPR\"/>\n                <constraint firstItem=\"ZN0-93-er7\" firstAttribute=\"height\" secondItem=\"u8n-2E-r2x\" secondAttribute=\"height\" id=\"LSQ-qv-Ez6\"/>\n                <constraint firstItem=\"ZN0-93-er7\" firstAttribute=\"top\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"bottom\" constant=\"-1\" id=\"PrO-R6-8lH\"/>\n                <constraint firstItem=\"u8n-2E-r2x\" firstAttribute=\"trailing\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"trailing\" constant=\"-1\" id=\"Ta8-19-xJL\"/>\n                <constraint firstItem=\"EqO-8K-pe7\" firstAttribute=\"centerY\" secondItem=\"yGP-qi-0QH\" secondAttribute=\"centerY\" id=\"ToA-bo-xmZ\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"trailing\" constant=\"30\" id=\"UWP-0i-c8r\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"aO3-09-OQh\" secondAttribute=\"trailing\" constant=\"30\" id=\"UX2-UW-uBS\"/>\n                <constraint firstItem=\"aO3-09-OQh\" firstAttribute=\"leading\" secondItem=\"bR4-fH-9Xy\" secondAttribute=\"trailing\" constant=\"8\" id=\"X8T-gH-5Wp\"/>\n                <constraint firstItem=\"u8n-2E-r2x\" firstAttribute=\"leading\" secondItem=\"ZN0-93-er7\" secondAttribute=\"trailing\" id=\"VvD-TW-gxj\"/>\n                <constraint firstItem=\"TUI-Vc-F84\" firstAttribute=\"leading\" secondItem=\"qtZ-jm-AmG\" secondAttribute=\"leading\" constant=\"30\" id=\"WfH-7m-ZQ0\"/>\n                <constraint firstItem=\"JHN-38-Vcl\" firstAttribute=\"centerY\" secondItem=\"Iqr-xS-F3Y\" secondAttribute=\"centerY\" id=\"Ykr-P9-gtI\"/>\n                <constraint firstItem=\"Iqr-xS-F3Y\" firstAttribute=\"top\" secondItem=\"yGP-qi-0QH\" secondAttribute=\"bottom\" constant=\"8\" id=\"fqN-kK-dal\"/>\n                <constraint firstItem=\"jyc-WA-HVd\" firstAttribute=\"leading\" secondItem=\"JHN-38-Vcl\" secondAttribute=\"trailing\" constant=\"1\" id=\"gnV-fL-hLc\"/>\n                <constraint firstItem=\"Thm-8g-Ol9\" firstAttribute=\"top\" secondItem=\"Iqr-xS-F3Y\" secondAttribute=\"bottom\" constant=\"30\" id=\"grH-KA-5Up\"/>\n                <constraint firstItem=\"gMn-n1-4Ff\" firstAttribute=\"top\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"bottom\" constant=\"-1\" id=\"iIT-xQ-ANp\"/>\n                <constraint firstItem=\"ZN0-93-er7\" firstAttribute=\"leading\" secondItem=\"gMn-n1-4Ff\" secondAttribute=\"trailing\" id=\"kU9-7e-70r\"/>\n                <constraint firstItem=\"nHi-Ex-f22\" firstAttribute=\"leading\" secondItem=\"jyc-WA-HVd\" secondAttribute=\"trailing\" constant=\"2\" id=\"mAv-pQ-Jje\"/>\n                <constraint firstItem=\"Iqr-xS-F3Y\" firstAttribute=\"trailing\" secondItem=\"yGP-qi-0QH\" secondAttribute=\"trailing\" id=\"mWK-me-z3c\"/>\n                <constraint firstItem=\"ZlK-na-Nuc\" firstAttribute=\"trailing\" secondItem=\"TUI-Vc-F84\" secondAttribute=\"trailing\" constant=\"1\" id=\"ohz-oA-8FE\"/>\n                <constraint firstItem=\"TUI-Vc-F84\" firstAttribute=\"top\" secondItem=\"Thm-8g-Ol9\" secondAttribute=\"bottom\" constant=\"30\" id=\"oyx-zk-NTd\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"yGP-qi-0QH\" secondAttribute=\"trailing\" constant=\"362\" id=\"ozf-uP-KJs\"/>\n                <constraint firstItem=\"aO3-09-OQh\" firstAttribute=\"top\" secondItem=\"ZlK-na-Nuc\" secondAttribute=\"bottom\" constant=\"10\" id=\"qZ1-ld-a0s\"/>\n                <constraint firstItem=\"bR4-fH-9Xy\" firstAttribute=\"top\" secondItem=\"ZlK-na-Nuc\" secondAttribute=\"bottom\" constant=\"10\" id=\"qZ2-ld-b1t\"/>\n                <constraint firstItem=\"jyc-WA-HVd\" firstAttribute=\"centerY\" secondItem=\"JHN-38-Vcl\" secondAttribute=\"centerY\" id=\"rDs-F6-0Jd\"/>\n                <constraint firstItem=\"Thm-8g-Ol9\" firstAttribute=\"centerX\" secondItem=\"qtZ-jm-AmG\" secondAttribute=\"centerX\" id=\"uNg-Kb-ces\"/>\n                <constraint firstItem=\"EqO-8K-pe7\" firstAttribute=\"leading\" secondItem=\"yGP-qi-0QH\" secondAttribute=\"trailing\" constant=\"8\" id=\"zLX-p4-NRg\"/>\n            </constraints>\n            <point key=\"canvasLocation\" x=\"533\" y=\"188\"/>\n        </customView>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"fHp-J5-akw\"/>\n    </objects>\n    <resources>\n        <image name=\"NSAddTemplate\" width=\"14\" height=\"13\"/>\n        <image name=\"NSRemoveTemplate\" width=\"14\" height=\"4\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/MousePreferencesViewController.swift",
    "content": "//\n//  MousePreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/29/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\n\nclass MousePreferencesViewController: NSViewController {}\n"
  },
  {
    "path": "Amethyst/Preferences/MousePreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"15702\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"15702\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"MousePreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"view\" destination=\"x3L-Gq-iaK\" id=\"Z03-U8-zUU\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customView id=\"x3L-Gq-iaK\" userLabel=\"General Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"548\" height=\"141\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"scP-ET-bFf\">\n                    <rect key=\"frame\" x=\"139\" y=\"95\" width=\"49\" height=\"16\"/>\n                    <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"right\" title=\"Mouse:\" id=\"XCu-B3-rM2\">\n                        <font key=\"font\" metaFont=\"system\"/>\n                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                    </textFieldCell>\n                </textField>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"wYk-HL-GMD\">\n                    <rect key=\"frame\" x=\"192\" y=\"94\" width=\"217\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Mouse follows focused windows\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"TxK-dN-e0P\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"0Zw-5D-d8F\" name=\"value\" keyPath=\"values.mouse-follows-focus\" id=\"oXG-n4-OKw\"/>\n                    </connections>\n                </button>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ixv-sr-c65\">\n                    <rect key=\"frame\" x=\"192\" y=\"72\" width=\"149\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Focus follows mouse\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"qfr-yK-eHB\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"0Zw-5D-d8F\" name=\"value\" keyPath=\"values.focus-follows-mouse\" id=\"M1V-sX-Exx\"/>\n                    </connections>\n                </button>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"sMl-QM-sUD\">\n                    <rect key=\"frame\" x=\"192\" y=\"50\" width=\"192\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Swap windows using mouse\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"uXT-sp-FfQ\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"0Zw-5D-d8F\" name=\"value\" keyPath=\"values.mouse-swaps-windows\" id=\"F7g-JO-UNF\"/>\n                    </connections>\n                </button>\n                <button translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0le-KQ-hcF\">\n                    <rect key=\"frame\" x=\"192\" y=\"28\" width=\"198\" height=\"18\"/>\n                    <buttonCell key=\"cell\" type=\"check\" title=\"Resize windows using mouse\" bezelStyle=\"regularSquare\" imagePosition=\"left\" state=\"on\" inset=\"2\" id=\"HHA-Vh-kIb\">\n                        <behavior key=\"behavior\" changeContents=\"YES\" doesNotDimImage=\"YES\" lightByContents=\"YES\"/>\n                        <font key=\"font\" metaFont=\"system\"/>\n                    </buttonCell>\n                    <connections>\n                        <binding destination=\"0Zw-5D-d8F\" name=\"value\" keyPath=\"values.mouse-resizes-windows\" id=\"hIZ-Jv-M3v\"/>\n                    </connections>\n                </button>\n            </subviews>\n            <constraints>\n                <constraint firstItem=\"0le-KQ-hcF\" firstAttribute=\"leading\" secondItem=\"sMl-QM-sUD\" secondAttribute=\"leading\" id=\"1RR-QQ-Sli\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"scP-ET-bFf\" secondAttribute=\"trailing\" constant=\"362\" id=\"8Y3-Sb-MQr\"/>\n                <constraint firstItem=\"ixv-sr-c65\" firstAttribute=\"leading\" secondItem=\"wYk-HL-GMD\" secondAttribute=\"leading\" id=\"FTL-tK-70n\"/>\n                <constraint firstItem=\"wYk-HL-GMD\" firstAttribute=\"leading\" secondItem=\"scP-ET-bFf\" secondAttribute=\"trailing\" constant=\"8\" id=\"IVN-cc-odd\"/>\n                <constraint firstItem=\"sMl-QM-sUD\" firstAttribute=\"leading\" secondItem=\"ixv-sr-c65\" secondAttribute=\"leading\" id=\"Qbp-uM-gRj\"/>\n                <constraint firstItem=\"scP-ET-bFf\" firstAttribute=\"top\" secondItem=\"x3L-Gq-iaK\" secondAttribute=\"top\" constant=\"30\" id=\"Xm0-Fe-eGV\"/>\n                <constraint firstItem=\"ixv-sr-c65\" firstAttribute=\"top\" secondItem=\"wYk-HL-GMD\" secondAttribute=\"bottom\" constant=\"8\" id=\"Yxr-99-91x\"/>\n                <constraint firstItem=\"wYk-HL-GMD\" firstAttribute=\"centerY\" secondItem=\"scP-ET-bFf\" secondAttribute=\"centerY\" id=\"ZZ2-vt-5oW\"/>\n                <constraint firstAttribute=\"bottom\" secondItem=\"0le-KQ-hcF\" secondAttribute=\"bottom\" constant=\"30\" id=\"fif-W9-08m\"/>\n                <constraint firstItem=\"sMl-QM-sUD\" firstAttribute=\"top\" secondItem=\"ixv-sr-c65\" secondAttribute=\"bottom\" constant=\"8\" id=\"hVp-9r-d09\"/>\n                <constraint firstItem=\"0le-KQ-hcF\" firstAttribute=\"top\" secondItem=\"sMl-QM-sUD\" secondAttribute=\"bottom\" constant=\"8\" id=\"zz4-Tg-iUB\"/>\n            </constraints>\n            <point key=\"canvasLocation\" x=\"561\" y=\"61\"/>\n        </customView>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"0Zw-5D-d8F\"/>\n    </objects>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesListItemView.swift",
    "content": "//\n//  ShortcutsPreferencesListItemView.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cartography\nimport Cocoa\nimport Foundation\nimport KeyboardShortcuts\n\nclass ShortcutsPreferencesListItemView: NSView {\n    private(set) var nameLabel: NSTextField?\n\n    override init(frame frameRect: NSRect) {\n        super.init(frame: frameRect)\n\n        let label = NSTextField()\n\n        label.isBezeled = false\n        label.isEditable = false\n        label.stringValue = \"\"\n        label.backgroundColor = NSColor.clear\n        label.sizeToFit()\n\n        addSubview(label)\n\n        constrain(label, self) { label, view in\n            label.centerY == view.centerY\n            label.left == view.left + 8\n        }\n\n        self.nameLabel = label\n    }\n\n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n\n    func setShortcutName(name: KeyboardShortcuts.Name) {\n        let shortcutView = KeyboardShortcuts.RecorderCocoa(for: name)\n\n        addSubview(shortcutView)\n\n        constrain(shortcutView, self) { shortcutView, view in\n            shortcutView.centerY == view.centerY\n            shortcutView.right == view.right - 16\n        }\n    }\n}\n"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesViewController.swift",
    "content": "//\n//  ShortcutsPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport KeyboardShortcuts\nimport Silica\n\nclass ShortcutsPreferencesViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {\n    private var hotKeyNameToDefaultsKey: [[String]] = []\n    @IBOutlet var tableView: NSTableView?\n\n    override func awakeFromNib() {\n        tableView?.dataSource = self\n        tableView?.delegate = self\n    }\n\n    override func viewWillAppear() {\n        super.viewWillAppear()\n\n        hotKeyNameToDefaultsKey = HotKeyManager<SIApplication>.hotKeyNameToDefaultsKey()\n        tableView?.reloadData()\n    }\n\n    func numberOfRows(in tableView: NSTableView) -> Int {\n        return hotKeyNameToDefaultsKey.count\n    }\n\n    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {\n        let frame = NSRect(x: 0, y: 0, width: tableView.frame.size.width, height: 30)\n        let shortcutItemView = ShortcutsPreferencesListItemView(frame: frame)\n        let name = hotKeyNameToDefaultsKey[row][0]\n        let key = hotKeyNameToDefaultsKey[row][1]\n\n        shortcutItemView.nameLabel?.stringValue = name\n        shortcutItemView.setShortcutName(name: KeyboardShortcuts.Name(key))\n\n        return shortcutItemView\n    }\n\n    func selectionShouldChange(in tableView: NSTableView) -> Bool {\n        return false\n    }\n}\n"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesViewController.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"15702\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"15702\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"ShortcutsPreferencesViewController\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"tableView\" destination=\"N3x-ad-oc8\" id=\"ARY-os-XHS\"/>\n                <outlet property=\"view\" destination=\"21f-NX-Gwr\" id=\"aWK-xd-gnE\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customView id=\"21f-NX-Gwr\" userLabel=\"Shortcut Preferences\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"548\" height=\"586\"/>\n            <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n            <subviews>\n                <box titlePosition=\"noTitle\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"ab5-OV-I22\">\n                    <rect key=\"frame\" x=\"27\" y=\"26\" width=\"494\" height=\"532\"/>\n                    <view key=\"contentView\" id=\"JEm-5T-by8\">\n                        <rect key=\"frame\" x=\"3\" y=\"3\" width=\"488\" height=\"526\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <scrollView autohidesScrollers=\"YES\" horizontalLineScroll=\"32\" horizontalPageScroll=\"10\" verticalLineScroll=\"32\" verticalPageScroll=\"10\" hasHorizontalScroller=\"NO\" usesPredominantAxisScrolling=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"OZI-RC-j8b\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"488\" height=\"526\"/>\n                                <clipView key=\"contentView\" id=\"AZ8-is-g0G\">\n                                    <rect key=\"frame\" x=\"1\" y=\"1\" width=\"486\" height=\"524\"/>\n                                    <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                    <subviews>\n                                        <tableView verticalHuggingPriority=\"750\" allowsExpansionToolTips=\"YES\" columnAutoresizingStyle=\"none\" selectionHighlightStyle=\"none\" columnReordering=\"NO\" columnResizing=\"NO\" multipleSelection=\"NO\" emptySelection=\"NO\" autosaveColumns=\"NO\" typeSelect=\"NO\" rowHeight=\"30\" viewBased=\"YES\" id=\"N3x-ad-oc8\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"486\" height=\"524\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                            <size key=\"intercellSpacing\" width=\"0.0\" height=\"2\"/>\n                                            <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <color key=\"gridColor\" name=\"gridColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                            <tableColumns>\n                                                <tableColumn editable=\"NO\" width=\"472\" minWidth=\"10\" maxWidth=\"3.4028234663852886e+38\" id=\"Hkg-O0-TNN\">\n                                                    <tableHeaderCell key=\"headerCell\" lineBreakMode=\"truncatingTail\" borderStyle=\"border\" alignment=\"left\">\n                                                        <font key=\"font\" metaFont=\"message\" size=\"11\"/>\n                                                        <color key=\"textColor\" name=\"headerTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                        <color key=\"backgroundColor\" name=\"headerColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                    </tableHeaderCell>\n                                                    <textFieldCell key=\"dataCell\" lineBreakMode=\"truncatingTail\" selectable=\"YES\" editable=\"YES\" alignment=\"left\" title=\"Text Cell\" id=\"z9h-CH-QkI\">\n                                                        <font key=\"font\" metaFont=\"system\"/>\n                                                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                        <color key=\"backgroundColor\" name=\"controlBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                    </textFieldCell>\n                                                    <tableColumnResizingMask key=\"resizingMask\" resizeWithTable=\"YES\"/>\n                                                    <prototypeCellViews>\n                                                        <tableCellView id=\"dEQ-9s-icb\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"1\" width=\"472\" height=\"17\"/>\n                                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                                            <subviews>\n                                                                <textField verticalHuggingPriority=\"750\" horizontalCompressionResistancePriority=\"250\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"SNR-eb-h74\">\n                                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"506\" height=\"17\"/>\n                                                                    <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                                                                    <textFieldCell key=\"cell\" lineBreakMode=\"truncatingTail\" sendsActionOnEndEditing=\"YES\" title=\"Table View Cell\" id=\"m0z-WS-aHR\">\n                                                                        <font key=\"font\" metaFont=\"system\"/>\n                                                                        <color key=\"textColor\" name=\"controlTextColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                                        <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                                                                    </textFieldCell>\n                                                                </textField>\n                                                            </subviews>\n                                                            <connections>\n                                                                <outlet property=\"textField\" destination=\"SNR-eb-h74\" id=\"0cG-r4-hbO\"/>\n                                                            </connections>\n                                                        </tableCellView>\n                                                    </prototypeCellViews>\n                                                </tableColumn>\n                                            </tableColumns>\n                                        </tableView>\n                                    </subviews>\n                                </clipView>\n                                <scroller key=\"horizontalScroller\" hidden=\"YES\" verticalHuggingPriority=\"750\" horizontal=\"YES\" id=\"s0W-ih-glN\">\n                                    <rect key=\"frame\" x=\"-100\" y=\"-100\" width=\"503\" height=\"15\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                </scroller>\n                                <scroller key=\"verticalScroller\" hidden=\"YES\" verticalHuggingPriority=\"750\" controlSize=\"mini\" horizontal=\"NO\" id=\"Sp7-9E-eeK\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"11\" height=\"0.0\"/>\n                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                </scroller>\n                            </scrollView>\n                        </subviews>\n                        <constraints>\n                            <constraint firstItem=\"OZI-RC-j8b\" firstAttribute=\"top\" secondItem=\"JEm-5T-by8\" secondAttribute=\"top\" id=\"7vc-3V-Fay\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"OZI-RC-j8b\" secondAttribute=\"trailing\" id=\"RB5-nm-4yQ\"/>\n                            <constraint firstItem=\"OZI-RC-j8b\" firstAttribute=\"leading\" secondItem=\"JEm-5T-by8\" secondAttribute=\"leading\" id=\"Z4Y-GN-H7F\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"OZI-RC-j8b\" secondAttribute=\"bottom\" id=\"zqG-Rd-5xo\"/>\n                        </constraints>\n                    </view>\n                </box>\n            </subviews>\n            <constraints>\n                <constraint firstAttribute=\"bottom\" secondItem=\"ab5-OV-I22\" secondAttribute=\"bottom\" constant=\"30\" id=\"L7k-Ug-K9c\"/>\n                <constraint firstAttribute=\"trailing\" secondItem=\"ab5-OV-I22\" secondAttribute=\"trailing\" constant=\"30\" id=\"P49-T9-2Ym\"/>\n                <constraint firstItem=\"ab5-OV-I22\" firstAttribute=\"leading\" secondItem=\"21f-NX-Gwr\" secondAttribute=\"leading\" constant=\"30\" id=\"eCr-ro-aZF\"/>\n                <constraint firstItem=\"ab5-OV-I22\" firstAttribute=\"top\" secondItem=\"21f-NX-Gwr\" secondAttribute=\"top\" constant=\"30\" id=\"vU5-yI-Oqq\"/>\n            </constraints>\n            <point key=\"canvasLocation\" x=\"131\" y=\"253\"/>\n        </customView>\n        <userDefaultsController id=\"XhK-Tn-zrU\"/>\n    </objects>\n</document>\n"
  },
  {
    "path": "Amethyst/Preferences/UserConfiguration.swift",
    "content": "//\n//  UserConfiguration.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport SwiftyJSON\nimport Yams\n\nenum DefaultFloat: Equatable {\n    case floating\n    case notFloating\n\n    fileprivate static func from(_ bool: Bool) -> DefaultFloat {\n        return bool ? .floating : .notFloating\n    }\n}\n\nprotocol ConfigurationStorage {\n    func object(forKey key: ConfigurationKey) -> Any?\n    func array(forKey key: ConfigurationKey) -> [Any]?\n    func bool(forKey key: ConfigurationKey) -> Bool\n    func float(forKey key: ConfigurationKey) -> Float\n    func stringArray(forKey key: ConfigurationKey) -> [String]?\n\n    func set(_ value: Any?, forKey key: ConfigurationKey)\n    func set(_ value: Bool, forKey key: ConfigurationKey)\n}\n\nextension UserDefaults: ConfigurationStorage {\n    func object(forKey key: ConfigurationKey) -> Any? {\n        return object(forKey: key.rawValue)\n    }\n\n    func array(forKey key: ConfigurationKey) -> [Any]? {\n        return array(forKey: key.rawValue)\n    }\n\n    func bool(forKey key: ConfigurationKey) -> Bool {\n        return bool(forKey: key.rawValue)\n    }\n\n    func float(forKey key: ConfigurationKey) -> Float {\n        return float(forKey: key.rawValue)\n    }\n\n    func stringArray(forKey key: ConfigurationKey) -> [String]? {\n        return stringArray(forKey: key.rawValue)\n    }\n\n    func set(_ value: Any?, forKey key: ConfigurationKey) {\n        set(value, forKey: key.rawValue)\n    }\n\n    func set(_ value: Bool, forKey key: ConfigurationKey) {\n        set(value, forKey: key.rawValue)\n    }\n}\n\nenum ConfigurationKey: String {\n    case layouts = \"layouts\"\n    case commandMod = \"mod\"\n    case commandKey = \"key\"\n    case mod1 = \"mod1\"\n    case mod2 = \"mod2\"\n    case mod3 = \"mod3\"\n    case mod4 = \"mod4\"\n    case windowMargins = \"window-margins\"\n    case smartWindowMargins = \"smart-window-margins\"\n    case windowMarginSize = \"window-margin-size\"\n    case windowMinimumHeight = \"window-minimum-height\"\n    case windowMinimumWidth = \"window-minimum-width\"\n    case windowMaxCount = \"window-max-count\"\n    case floatingBundleIdentifiers = \"floating\"\n    case floatingBundleIdentifiersIsBlacklist = \"floating-is-blacklist\"\n    case ignoreMenuBar = \"ignore-menu-bar\"\n    case floatSmallWindows = \"float-small-windows\"\n    case smallWindowSize = \"small-window-size\"\n    case mouseFollowsFocus = \"mouse-follows-focus\"\n    case focusFollowsMouse = \"focus-follows-mouse\"\n    case mouseSwapsWindows = \"mouse-swaps-windows\"\n    case mouseResizesWindows = \"mouse-resizes-windows\"\n    case layoutHUD = \"enables-layout-hud\"\n    case layoutHUDOnSpaceChange = \"enables-layout-hud-on-space-change\"\n    case windowCountHUD = \"enables-window-count-hud\"\n    case useCanaryBuild = \"use-canary-build\"\n    case newWindowsToMain = \"new-windows-to-main\"\n    case followSpaceThrownWindows = \"follow-space-thrown-windows\"\n    case windowResizeStep = \"window-resize-step\"\n    case screenPaddingLeft = \"screen-padding-left\"\n    case screenPaddingRight = \"screen-padding-right\"\n    case screenPaddingTop = \"screen-padding-top\"\n    case screenPaddingBottom = \"screen-padding-bottom\"\n    case debugLayoutInfo = \"debug-layout-info\"\n    case restoreLayoutsOnLaunch = \"restore-layouts-on-launch\"\n    case disablePaddingOnBuiltinDisplay = \"disable-padding-on-builtin-display\"\n    case hideMenuBarIcon = \"hide-menu-bar-icon\"\n}\n\nextension ConfigurationKey: CaseIterable {}\n\nenum CommandKey: String {\n    case cycleLayoutForward = \"cycle-layout\"\n    case cycleLayoutBackward = \"cycle-layout-backward\"\n    case shrinkMain = \"shrink-main\"\n    case expandMain = \"expand-main\"\n    case increaseMain = \"increase-main\"\n    case decreaseMain = \"decrease-main\"\n    case command1 = \"command1\"\n    case command2 = \"command2\"\n    case command3 = \"command3\"\n    case command4 = \"command4\"\n    case focusCCW = \"focus-ccw\"\n    case focusCW = \"focus-cw\"\n    case focusMain = \"focus-main\"\n    case focusScreenCCW = \"focus-screen-ccw\"\n    case focusScreenCW = \"focus-screen-cw\"\n    case swapScreenCCW = \"swap-screen-ccw\"\n    case swapScreenCW = \"swap-screen-cw\"\n    case swapCCW = \"swap-ccw\"\n    case swapCW = \"swap-cw\"\n    case swapMain = \"swap-main\"\n    case throwSpacePrefix = \"throw-space\"\n    case focusScreenPrefix = \"focus-screen\"\n    case throwScreenPrefix = \"throw-screen\"\n    case throwSpaceLeft = \"throw-space-left\"\n    case throwSpaceRight = \"throw-space-right\"\n    case toggleFloat = \"toggle-float\"\n    case displayCurrentLayout = \"display-current-layout\"\n    case toggleTiling = \"toggle-tiling\"\n    case enableTiling = \"enable-tiling\"\n    case disableTiling = \"disable-tiling\"\n    case reevaluateWindows = \"reevaluate-windows\"\n    case toggleFocusFollowsMouse = \"toggle-focus-follows-mouse\"\n    case relaunchAmethyst = \"relaunch-amethyst\"\n    case increaseWindowMaxCount = \"increase-window-max-count\"\n    case decreaseWindowMaxCount = \"decrease-window-max-count\"\n}\n\nprotocol UserConfigurationDelegate: AnyObject {\n    func configurationGlobalTilingDidChange(_ userConfiguration: UserConfiguration)\n    func configurationAccessibilityPermissionsDidChange(_ userConfiguration: UserConfiguration)\n}\n\nclass FloatingBundle: NSObject {\n    @objc dynamic let id: String\n    @objc dynamic let windowTitles: [String]\n\n    init(id: String, windowTitles: [String]) {\n        self.id = id\n        self.windowTitles = windowTitles\n    }\n\n    override func isEqual(_ object: Any?) -> Bool {\n        guard let other = object as? FloatingBundle else {\n            return false\n        }\n\n        return other.id == id && other.windowTitles == windowTitles\n    }\n\n    func encoded() -> Any {\n        return [\n            \"id\": id,\n            \"window-titles\": windowTitles\n        ]\n    }\n\n    static func from(_ object: Any) -> FloatingBundle? {\n        if let id = object as? String {\n            return FloatingBundle(id: id, windowTitles: [])\n        } else if let dict = object as? [String: Any] {\n            var json = JSON(dict)\n\n            if let id = json[\"id\"].string, let windowTitles = json[\"window-titles\"].arrayObject as? [String] {\n                return FloatingBundle(id: id, windowTitles: windowTitles)\n            }\n\n            guard let key = dict.keys.first, dict.count == 1 else {\n                return nil\n            }\n\n            json = json[key]\n\n            if let windowTitles = json[\"window-titles\"].arrayObject as? [String] {\n                return FloatingBundle(id: key, windowTitles: windowTitles)\n            }\n\n            return nil\n        } else {\n            return nil\n        }\n    }\n}\n\nclass UserConfiguration: NSObject {\n    static let shared = UserConfiguration()\n    private let storage: ConfigurationStorage\n\n    weak var delegate: UserConfigurationDelegate?\n\n    var tilingEnabled = true {\n        didSet {\n            delegate?.configurationGlobalTilingDidChange(self)\n        }\n    }\n    var hasAccessibilityPermissions = true {\n        didSet {\n            delegate?.configurationAccessibilityPermissionsDidChange(self)\n        }\n    }\n\n    var configurationYAML: [String: Any]?\n    var configurationJSON: JSON?\n    var defaultConfiguration: JSON?\n\n    var modifier1: AMModifierFlags?\n    var modifier2: AMModifierFlags?\n    var modifier3: AMModifierFlags?\n    var modifier4: AMModifierFlags?\n\n    init(storage: ConfigurationStorage) {\n        self.storage = storage\n    }\n\n    override convenience init() {\n        self.init(storage: UserDefaults.standard)\n    }\n\n    private func configurationValueForKey<T>(_ key: ConfigurationKey, fallbackToDefault: Bool = true) -> T? {\n        return configurationValue(forKeyValue: key.rawValue, fallbackToDefault: fallbackToDefault)\n    }\n\n    private func configurationValue<T>(forKeyValue keyValue: String, fallbackToDefault: Bool = true) -> T? {\n        if let yamlValue = configurationYAML?[keyValue] {\n            if yamlValue is NSNull {\n                return nil\n            } else {\n                return yamlValue as? T\n            }\n        }\n\n        if let jsonValue = configurationJSON?[keyValue], jsonValue.exists(), jsonValue.error == nil {\n            return jsonValue.rawValue as? T\n        }\n\n        if fallbackToDefault {\n            return defaultConfiguration![keyValue].rawValue as? T\n        }\n\n        return nil\n    }\n\n    func modifierFlagsForStrings(_ modifierStrings: [String]) -> AMModifierFlags {\n        var flags: NSEvent.ModifierFlags = []\n        for modifierString in modifierStrings {\n            switch modifierString {\n            case \"option\":\n                flags.insert(.option)\n            case \"shift\":\n                flags.insert(.shift)\n            case \"control\":\n                flags.insert(.control)\n            case \"command\":\n                flags.insert(.command)\n            default:\n                log.warning(\"Unrecognized modifier string: \\(modifierString)\")\n            }\n        }\n        return flags\n    }\n\n    func load() {\n        let hasAccessibilityPermissions = confirmAccessibilityPermissions()\n        if self.hasAccessibilityPermissions != hasAccessibilityPermissions {\n            self.hasAccessibilityPermissions = hasAccessibilityPermissions\n        }\n        loadConfigurationFile()\n        loadConfiguration()\n    }\n\n    func loadConfiguration() {\n        for key in ConfigurationKey.allCases {\n            let value: Any? = configurationValueForKey(key, fallbackToDefault: false)\n            let defaultValue = defaultConfiguration?[key.rawValue]\n            let existingValue = storage.object(forKey: key)\n\n            let hasLocalConfigurationValue = value != nil\n            let hasDefaultConfigurationValue = (defaultValue != nil && defaultValue?.error == nil)\n            let hasExistingValue = (existingValue != nil)\n\n            guard hasLocalConfigurationValue || (hasDefaultConfigurationValue && !hasExistingValue) else {\n                continue\n            }\n\n            storage.set(hasLocalConfigurationValue ? value : defaultValue?.rawValue, forKey: key)\n        }\n    }\n\n    private func yamlForConfig(at path: String) -> [String: Any]? {\n        guard FileManager.default.fileExists(atPath: path, isDirectory: nil) else {\n            return nil\n        }\n\n        let configPath = URL(fileURLWithPath: path)\n\n        guard let string = try? String(contentsOf: configPath) else {\n            return nil\n        }\n\n        do {\n            let yaml = try Yams.load(yaml: string)\n            return yaml as? [String: Any]\n        } catch {\n            log.debug(error)\n            return nil\n        }\n    }\n\n    private func jsonForConfig(at path: String) -> JSON? {\n        guard FileManager.default.fileExists(atPath: path, isDirectory: nil) else {\n            return nil\n        }\n\n        let configPath = URL(fileURLWithPath: path)\n\n        guard let data = try? Data(contentsOf: configPath) else {\n            return nil\n        }\n\n        return try? JSON(data: data)\n    }\n\n    private func loadConfigurationFile() {\n        let xdgConfigPath = ProcessInfo.processInfo.environment[\"XDG_CONFIG_HOME\"] ?? NSHomeDirectory().appending(\"/.config\")\n        let amethystXDGConfigPath = xdgConfigPath.appending(\"/amethyst\")\n        let amethystYAMLConfigPath = NSHomeDirectory().appending(\"/.amethyst.yml\")\n        let amethystJSONConfigPath = NSHomeDirectory().appending(\"/.amethyst\")\n        let defaultAmethystConfigPath = Bundle.main.path(forResource: \"default\", ofType: \"amethyst\")\n\n        var isDirectory: ObjCBool = false\n        /**\n         Prioritiy order for config files:\n         1. yml in home dir\n         2. yml in xdg path\n         3. json in home dir\n         4. default json\n         */\n        if FileManager.default.fileExists(atPath: amethystYAMLConfigPath, isDirectory: &isDirectory) {\n            configurationYAML = yamlForConfig(at: amethystYAMLConfigPath)\n\n            if configurationYAML == nil {\n                log.error(\"error loading configuration as yaml\")\n\n                let alert = NSAlert()\n                alert.alertStyle = .critical\n                alert.messageText = \"Error loading configuration\"\n                alert.runModal()\n            }\n        } else if FileManager.default.fileExists(atPath: amethystXDGConfigPath, isDirectory: &isDirectory) {\n            configurationYAML = yamlForConfig(at: isDirectory.boolValue ? amethystXDGConfigPath.appending(\"/amethyst.yml\") : amethystXDGConfigPath)\n\n            if configurationYAML == nil {\n                log.error(\"error loading configuration as yaml\")\n\n                let alert = NSAlert()\n                alert.alertStyle = .critical\n                alert.messageText = \"Error loading configuration\"\n                alert.runModal()\n            }\n        } else if FileManager.default.fileExists(atPath: amethystJSONConfigPath, isDirectory: &isDirectory) {\n            configurationJSON = jsonForConfig(at: amethystJSONConfigPath)\n\n            if configurationJSON == nil {\n                log.error(\"error loading configuration as json\")\n\n                let alert = NSAlert()\n                alert.alertStyle = .critical\n                alert.messageText = \"Error loading configuration\"\n                alert.runModal()\n            }\n        }\n\n        defaultConfiguration = jsonForConfig(at: defaultAmethystConfigPath ?? \"\")\n        if defaultConfiguration == nil {\n            log.error(\"error loading default configuration\")\n\n            let alert = NSAlert()\n            alert.alertStyle = .critical\n            alert.messageText = \"Error loading default configuration\"\n            alert.runModal()\n        }\n\n        let mod1Strings: [String] = configurationValueForKey(.mod1) ?? []\n        let mod2Strings: [String] = configurationValueForKey(.mod2) ?? []\n        let mod3Strings: [String]? = configurationValueForKey(.mod3)\n        let mod4Strings: [String]? = configurationValueForKey(.mod4)\n\n        modifier1 = modifierFlagsForStrings(mod1Strings)\n        modifier2 = modifierFlagsForStrings(mod2Strings)\n\n        if modifier1 == nil || modifier1!.isEmpty {\n            log.error(\"error loading a mod1\")\n\n            let alert = NSAlert()\n            alert.alertStyle = .critical\n            alert.messageText = \"Error loading mod1\"\n            alert.runModal()\n        }\n\n        if modifier2 == nil || modifier2!.isEmpty {\n            log.error(\"error loading a mod2\")\n\n            let alert = NSAlert()\n            alert.alertStyle = .critical\n            alert.messageText = \"Error loading mod2\"\n            alert.runModal()\n        }\n\n        if let mod3Strings = mod3Strings {\n            modifier3 = modifierFlagsForStrings(mod3Strings)\n        }\n\n        if let mod4Strings = mod4Strings {\n            modifier4 = modifierFlagsForStrings(mod4Strings)\n        }\n    }\n\n    static func constructLayoutKeyString(_ layoutKey: String) -> String {\n        return \"select-\\(layoutKey)-layout\"\n    }\n\n    func constructCommand(for hotKeyRegistrar: HotKeyRegistrar, commandKey: String, handler: @escaping HotKeyHandler) {\n        var override = false\n        var command: [String: String]? = configurationValue(forKeyValue: commandKey, fallbackToDefault: false)\n        if command != nil {\n            override = true\n        } else if let enabled: Bool = configurationValue(forKeyValue: commandKey, fallbackToDefault: false), !enabled {\n            override = true\n            command = nil\n        } else {\n            let mod1: [String]? = configurationValueForKey(.mod1, fallbackToDefault: false)\n            let mod2: [String]? = configurationValueForKey(.mod2, fallbackToDefault: false)\n            let mod3: [String]? = configurationValueForKey(.mod3, fallbackToDefault: false)\n            let mod4: [String]? = configurationValueForKey(.mod4, fallbackToDefault: false)\n            if mod1 != nil || mod2 != nil || mod3 != nil || mod4 != nil {\n                override = true\n            }\n            command = defaultConfiguration?[commandKey].rawValue as? [String: String]\n        }\n\n        let commandKeyString = command?[ConfigurationKey.commandKey.rawValue]\n        let commandModifierString = command?[ConfigurationKey.commandMod.rawValue]\n\n        var commandFlags: AMModifierFlags?\n\n        if let modifierString = commandModifierString {\n            switch modifierString {\n            case \"mod1\":\n                commandFlags = modifier1\n            case \"mod2\":\n                commandFlags = modifier2\n            case \"mod3\":\n                commandFlags = modifier3\n            case \"mod4\":\n                commandFlags = modifier4\n            default:\n                log.warning(\"Unknown modifier string: \\(modifierString)\")\n                return\n            }\n        }\n\n        let injectedHandler: () -> Void = { [weak self] in\n            guard let `self` = self else {\n                return\n            }\n\n            let hasAccessibilityPermissions = self.confirmAccessibilityPermissions()\n\n            if self.hasAccessibilityPermissions != hasAccessibilityPermissions {\n                self.hasAccessibilityPermissions = hasAccessibilityPermissions\n            }\n\n            guard hasAccessibilityPermissions else {\n                return\n            }\n\n            DispatchQueue.global(qos: .userInitiated).async {\n                handler()\n            }\n        }\n\n        hotKeyRegistrar.registerHotKey(\n            with: commandKeyString,\n            modifiers: commandFlags,\n            handler: injectedHandler,\n            defaultsKey: commandKey,\n            override: override\n        )\n    }\n\n    func hasCustomConfiguration() -> Bool {\n        return configurationYAML != nil || configurationJSON != nil\n    }\n\n    private func modifierFlagsForModifierString(_ modifierString: String) -> AMModifierFlags {\n        switch modifierString {\n        case \"mod1\":\n            return modifier1!\n        case \"mod2\":\n            return modifier2!\n        case \"mod3\":\n            return modifier3!\n        case \"mod4\":\n            return modifier4!\n        default:\n            log.warning(\"Unknown modifier string: \\(modifierString)\")\n            return modifier1!\n        }\n    }\n\n    func layoutKeys() -> [String] {\n        let layoutKeys = storage.array(forKey: .layouts) as? [String]\n        return layoutKeys ?? []\n    }\n\n    func setLayoutKeys(_ layoutKeys: [String]) {\n        storage.set(layoutKeys as Any?, forKey: .layouts)\n    }\n\n    func runningApplication(_ runningApplication: BundleIdentifiable, byDefaultFloatsForTitle title: String?) -> Reliable<DefaultFloat> {\n        let useIdentifiersAsBlacklist = floatingBundleIdentifiersIsBlacklist()\n\n        // If the application is in the floating list we need to continue to check title\n        // Otherwise\n        //   - Blacklist means not floating\n        //   - Whitelist menas floating\n        guard let floatingBundle = runningApplicationFloatingBundle(runningApplication) else {\n            return .reliable(DefaultFloat.from(!useIdentifiersAsBlacklist))\n        }\n\n        // If the window list is empty then all windows are included in the list\n        //   - Blacklist means floating\n        //   - Whitelist means not floating\n        if floatingBundle.windowTitles.isEmpty {\n            return .reliable(DefaultFloat.from(useIdentifiersAsBlacklist))\n        }\n\n        // If the title is `nil` then we cannot make a determination so we fall back to the default. However, we have to treat this value as unreliable as the window could have just been created and be in the process of loading.\n        guard let title = title else {\n            return .unreliable(DefaultFloat.from(!useIdentifiersAsBlacklist))\n        }\n\n        // If the title matches it is included\n        //   - Blacklist means floating\n        //   - Whitelist means not floating\n        if floatingBundle.windowTitles.contains(where: { windowTitle in\n            if title.range(of: windowTitle, options: .regularExpression) != nil {\n                return true\n            } else {\n                return false\n            }\n        }) {\n            return .reliable(DefaultFloat.from(useIdentifiersAsBlacklist))\n        }\n\n        // Otherwise the window is not included\n        //   - Blacklist means not floating\n        //   - Whitelist means floating\n        let defaultFloat = DefaultFloat.from(!useIdentifiersAsBlacklist)\n\n        // If the title is empty the window could have just been created and in the process of loading. Our float determination could still be correct, but to account for the potential change we mark it as unreliable.\n        if title.isEmpty {\n            return .unreliable(defaultFloat)\n        }\n\n        return .reliable(defaultFloat)\n    }\n\n    func runningApplicationFloatingBundle(_ runningApplication: BundleIdentifiable) -> FloatingBundle? {\n        let floatingBundles = self.floatingBundles()\n\n        let bundleIdentifier = runningApplication.bundleIdentifier ?? \"\"\n\n        for floatingBundle in floatingBundles {\n            if floatingBundle.id.contains(\"*\") {\n                do {\n                    let pattern = floatingBundle.id\n                        .replacingOccurrences(of: \".\", with: \"\\\\.\")\n                        .replacingOccurrences(of: \"*\", with: \".*\")\n                    let regex = try NSRegularExpression(pattern: \"^\\(pattern)$\", options: [])\n                    let fullRange = NSRange(location: 0, length: bundleIdentifier.count)\n\n                    if regex.firstMatch(in: bundleIdentifier, options: [], range: fullRange) != nil {\n                        return floatingBundle\n                    }\n                } catch {\n                    continue\n                }\n            } else {\n                if floatingBundle.id == runningApplication.bundleIdentifier {\n                    return floatingBundle\n                }\n            }\n        }\n\n        return nil\n    }\n\n    func ignoreMenuBar() -> Bool {\n        return storage.bool(forKey: .ignoreMenuBar)\n    }\n\n    func floatSmallWindows() -> Bool {\n        return storage.bool(forKey: .floatSmallWindows)\n    }\n\n    func smallWindowSize() -> CGFloat {\n        let size = CGFloat(storage.float(forKey: .smallWindowSize))\n        return size > 0 ? size : 500\n    }\n\n    func mouseFollowsFocus() -> Bool {\n        return storage.bool(forKey: .mouseFollowsFocus)\n    }\n\n    func focusFollowsMouse() -> Bool {\n        return storage.bool(forKey: .focusFollowsMouse)\n    }\n\n    func toggleFocusFollowsMouse() {\n        storage.set(!focusFollowsMouse(), forKey: .focusFollowsMouse)\n    }\n\n    func mouseSwapsWindows() -> Bool {\n        return storage.bool(forKey: .mouseSwapsWindows)\n    }\n\n    func mouseResizesWindows() -> Bool {\n        return storage.bool(forKey: .mouseResizesWindows)\n    }\n\n    func enablesLayoutHUD() -> Bool {\n        return storage.bool(forKey: .layoutHUD)\n    }\n\n    func enablesLayoutHUDOnSpaceChange() -> Bool {\n        return storage.bool(forKey: .layoutHUDOnSpaceChange)\n    }\n\n    func enablesWindowCountHUD() -> Bool {\n        return storage.bool(forKey: .windowCountHUD)\n    }\n\n    func useCanaryBuild() -> Bool {\n        return storage.bool(forKey: .useCanaryBuild)\n    }\n\n    func windowMarginSize() -> CGFloat {\n        return CGFloat(storage.float(forKey: .windowMarginSize))\n    }\n\n    func windowMargins() -> Bool {\n        if !storage.bool(forKey: .windowMargins) {\n            return false\n        }\n        // if smartWindowMargins is not enabled, enable window margins\n        if !smartWindowMargins() {\n            return true\n        }\n        // if smartWindowMargins is enabled, enabled window margins if there are more than one visible windows on screen\n        let options = CGWindowListOption(arrayLiteral: .excludeDesktopElements, .optionOnScreenOnly)\n        let windowsListInfo = CGWindowListCopyWindowInfo(options, CGWindowID(0))\n        let infoList = windowsListInfo as! [[String: Any]]\n        let visibleWindows = infoList.filter { $0[\"kCGWindowLayer\"] as! Int == 0 }\n        return visibleWindows.count > 1\n    }\n\n    func smartWindowMargins() -> Bool {\n        return storage.bool(forKey: .smartWindowMargins)\n    }\n\n    func windowMinimumHeight() -> CGFloat {\n        return CGFloat(storage.float(forKey: .windowMinimumHeight))\n    }\n\n    func windowMinimumWidth() -> CGFloat {\n        return CGFloat(storage.float(forKey: .windowMinimumWidth))\n    }\n\n    func windowMaxCount() -> Int? {\n        let int = Int(storage.float(forKey: .windowMaxCount))\n        return int == 0 ? nil : int\n    }\n\n    private func setConfigurationValueWithKVO(_ value: Any?, forKey key: ConfigurationKey) {\n        if let userDefaults = storage as? UserDefaults {\n            userDefaults.willChangeValue(forKey: key.rawValue)\n            userDefaults.set(value, forKey: key)\n            userDefaults.didChangeValue(forKey: key.rawValue)\n        } else {\n            storage.set(value, forKey: key)\n        }\n    }\n\n    func increaseWindowMaxCount() {\n        let currentCount = windowMaxCount() ?? 0\n        let newCount = currentCount + 1\n\n        setConfigurationValueWithKVO(Float(newCount), forKey: .windowMaxCount)\n    }\n\n    func decreaseWindowMaxCount() {\n        let currentCount = windowMaxCount() ?? 1\n        let newCount = max(0, currentCount - 1)\n\n        setConfigurationValueWithKVO(Float(newCount), forKey: .windowMaxCount)\n    }\n\n    func windowResizeStep() -> CGFloat {\n        return CGFloat(storage.float(forKey: .windowResizeStep) / 100.0)\n    }\n\n    func screenPaddingTop() -> CGFloat {\n        return CGFloat(storage.float(forKey: .screenPaddingTop))\n    }\n\n    func screenPaddingBottom() -> CGFloat {\n        return CGFloat(storage.float(forKey: .screenPaddingBottom))\n    }\n\n    func screenPaddingLeft() -> CGFloat {\n        return CGFloat(storage.float(forKey: .screenPaddingLeft))\n    }\n\n    func screenPaddingRight() -> CGFloat {\n        return CGFloat(storage.float(forKey: .screenPaddingRight))\n    }\n\n    func floatingBundleIdentifiersIsBlacklist() -> Bool {\n        guard storage.object(forKey: .floatingBundleIdentifiersIsBlacklist) != nil else {\n            return true\n        }\n        return storage.bool(forKey: .floatingBundleIdentifiersIsBlacklist)\n    }\n\n    func floatingBundles() -> [FloatingBundle] {\n        guard let floatingBundles = storage.array(forKey: .floatingBundleIdentifiers) else {\n            return []\n        }\n\n        return floatingBundles.compactMap { FloatingBundle.from($0) }\n    }\n\n    func setFloatingBundles(_ floatingBundles: [FloatingBundle]) {\n        storage.set(floatingBundles.map { $0.encoded() }, forKey: .floatingBundleIdentifiers)\n    }\n\n    func sendNewWindowsToMainPane() -> Bool {\n        return storage.bool(forKey: .newWindowsToMain)\n    }\n\n    func disablePaddingOnBuiltinDisplay() -> Bool {\n        return storage.bool(forKey: .disablePaddingOnBuiltinDisplay)\n    }\n\n    func followWindowsThrownBetweenSpaces() -> Bool {\n        return storage.bool(forKey: .followSpaceThrownWindows)\n    }\n\n    func restoreLayoutsOnLaunch() -> Bool {\n        return storage.bool(forKey: .restoreLayoutsOnLaunch)\n    }\n\n    func hideMenuBarIcon() -> Bool {\n        return storage.bool(forKey: .hideMenuBarIcon)\n    }\n}\n\nextension UserConfiguration {\n    @discardableResult func confirmAccessibilityPermissions() -> Bool {\n        let options = [\n            kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true\n        ]\n\n        return AXIsProcessTrustedWithOptions(options as CFDictionary)\n    }\n}\n"
  },
  {
    "path": "Amethyst/View/LayoutNameWindow.swift",
    "content": "//\n//  LayoutNameWindow.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Cocoa\nimport Foundation\nimport QuartzCore\n\nclass LayoutNameWindow: NSWindow {\n    @IBOutlet weak var layoutNameField: NSTextField?\n    @IBOutlet weak var layoutDescriptionLabel: NSTextField?\n\n    @IBOutlet override var contentView: NSView? {\n        didSet {\n            contentView?.wantsLayer = true\n            contentView?.layer?.frame = NSRectToCGRect(contentView!.frame)\n            contentView?.layer?.cornerRadius = 20.0\n            contentView?.layer?.masksToBounds = true\n            contentView?.layer?.backgroundColor = NSColor.black.withAlphaComponent(0.75).cgColor\n        }\n    }\n    @IBOutlet var containerView: NSView?\n\n    override func awakeFromNib() {\n        super.awakeFromNib()\n\n        isOpaque = false\n        ignoresMouseEvents = true\n        backgroundColor = NSColor.clear\n        level = .floating\n    }\n\n    // Display custom notification with dynamic sizing\n    func displayNotification(title: String, description: String) {\n        // layoutDescriptionLabel?.isHidden = false\n        layoutNameField?.stringValue = title\n        layoutDescriptionLabel?.stringValue = description\n\n        // Calculate size needed for both name and description\n        let longerText = title.count > description.count ? title : description\n        resizeToFitText(text: longerText)\n    }\n\n    // Dynamic window resizing based on text content\n    private func resizeToFitText(text: String) {\n        guard let textField = layoutNameField else { return }\n\n        // Calculate text width with current font\n        let font = textField.font ?? NSFont.systemFont(ofSize: 20)\n        let textSize = text.size(withAttributes: [.font: font])\n\n        // Add padding (40px on each side + extra space)\n        let minWidth: CGFloat = 200\n        let maxWidth: CGFloat = 500\n        let padding: CGFloat = 80\n        let calculatedWidth = textSize.width + padding\n\n        // Constrain width between min and max\n        let newWidth = max(minWidth, min(maxWidth, calculatedWidth))\n\n        // Keep the same height\n        let currentFrame = frame\n        let newFrame = NSRect(\n            x: currentFrame.origin.x,\n            y: currentFrame.origin.y,\n            width: newWidth,\n            height: currentFrame.height\n        )\n\n        setFrame(newFrame, display: true, animate: false)\n\n        // Update content view layer frame\n        contentView?.layer?.frame = NSRectToCGRect(contentView!.frame)\n    }\n}\n"
  },
  {
    "path": "Amethyst/View/LayoutNameWindow.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14460.31\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14460.31\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"LayoutNameWIndowController\">\n            <connections>\n                <outlet property=\"window\" destination=\"1\" id=\"1No-t3-YBY\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <window allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" hasShadow=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"1\" customClass=\"LayoutNameWindow\" customModule=\"Amethyst\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <windowPositionMask key=\"initialPositionMask\" leftStrut=\"YES\" rightStrut=\"YES\" topStrut=\"YES\" bottomStrut=\"YES\"/>\n            <rect key=\"contentRect\" x=\"196\" y=\"240\" width=\"285\" height=\"64\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"1440\" height=\"877\"/>\n            <view key=\"contentView\" id=\"2\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"285\" height=\"64\"/>\n                <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                <subviews>\n                    <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YXF-Df-V9n\">\n                        <rect key=\"frame\" x=\"-2\" y=\"20\" width=\"289\" height=\"24\"/>\n                        <textFieldCell key=\"cell\" scrollable=\"YES\" lineBreakMode=\"clipping\" sendsActionOnEndEditing=\"YES\" alignment=\"center\" title=\"Layout Name\" id=\"WEZ-6u-WYA\">\n                            <font key=\"font\" metaFont=\"system\" size=\"20\"/>\n                            <color key=\"textColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"calibratedRGB\"/>\n                            <color key=\"backgroundColor\" name=\"controlColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        </textFieldCell>\n                    </textField>\n                    <imageView horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" fixedFrame=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"rF1-PP-zpf\">\n                        <rect key=\"frame\" x=\"8\" y=\"33\" width=\"17\" height=\"25\"/>\n                        <autoresizingMask key=\"autoresizingMask\" flexibleMaxX=\"YES\" flexibleMinY=\"YES\"/>\n                        <imageCell key=\"cell\" refusesFirstResponder=\"YES\" alignment=\"left\" imageScaling=\"proportionallyDown\" image=\"amethyst\" id=\"Iyr-W5-Usa\"/>\n                    </imageView>\n                    <textField horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"750\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"UBl-Tk-jEi\" userLabel=\"Layout Description Field\">\n                        <rect key=\"frame\" x=\"-2\" y=\"4\" width=\"289\" height=\"14\"/>\n                        <textFieldCell key=\"cell\" lineBreakMode=\"clipping\" alignment=\"center\" title=\"Layout Description\" id=\"fnu-hV-9uY\">\n                            <font key=\"font\" metaFont=\"smallSystem\"/>\n                            <color key=\"textColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                            <color key=\"backgroundColor\" name=\"textBackgroundColor\" catalog=\"System\" colorSpace=\"catalog\"/>\n                        </textFieldCell>\n                        <connections>\n                            <binding destination=\"UXh-pR-GPs\" name=\"hidden\" keyPath=\"values.debug-layout-info\" id=\"aEw-Xh-xc7\">\n                                <dictionary key=\"options\">\n                                    <string key=\"NSValueTransformerName\">NSNegateBoolean</string>\n                                </dictionary>\n                            </binding>\n                        </connections>\n                    </textField>\n                </subviews>\n                <constraints>\n                    <constraint firstAttribute=\"trailing\" secondItem=\"UBl-Tk-jEi\" secondAttribute=\"trailing\" id=\"6cm-LY-dVx\"/>\n                    <constraint firstItem=\"UBl-Tk-jEi\" firstAttribute=\"leading\" secondItem=\"2\" secondAttribute=\"leading\" id=\"JzF-eQ-sRc\"/>\n                    <constraint firstAttribute=\"trailing\" secondItem=\"YXF-Df-V9n\" secondAttribute=\"trailing\" id=\"T51-1f-4oF\"/>\n                    <constraint firstAttribute=\"bottom\" secondItem=\"UBl-Tk-jEi\" secondAttribute=\"bottom\" constant=\"4\" id=\"g4h-XK-qFX\"/>\n                    <constraint firstItem=\"YXF-Df-V9n\" firstAttribute=\"centerY\" secondItem=\"2\" secondAttribute=\"centerY\" id=\"sgp-Bu-Qj5\"/>\n                    <constraint firstItem=\"YXF-Df-V9n\" firstAttribute=\"leading\" secondItem=\"2\" secondAttribute=\"leading\" id=\"zfm-cP-4Z0\"/>\n                </constraints>\n            </view>\n            <connections>\n                <outlet property=\"containerView\" destination=\"2\" id=\"ZO9-Uz-ux8\"/>\n                <outlet property=\"contentView\" destination=\"2\" id=\"9oa-GD-j63\"/>\n                <outlet property=\"layoutDescriptionLabel\" destination=\"UBl-Tk-jEi\" id=\"OAb-DD-628\"/>\n                <outlet property=\"layoutNameField\" destination=\"YXF-Df-V9n\" id=\"KfT-Yb-Yh3\"/>\n            </connections>\n            <point key=\"canvasLocation\" x=\"255.5\" y=\"295\"/>\n        </window>\n        <userDefaultsController representsSharedInstance=\"YES\" id=\"UXh-pR-GPs\"/>\n    </objects>\n    <resources>\n        <image name=\"amethyst\" width=\"32\" height=\"32\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Amethyst/View/LayoutNameWindowController.swift",
    "content": "//\n//  LayoutNameWIndowController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 1/16/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\n\nclass LayoutNameWindowController: NSWindowController {\n\n}\n"
  },
  {
    "path": "Amethyst/View/PreferencesWindow.swift",
    "content": "//\n//  PreferencesWindow.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 7/19/17.\n//  Copyright © 2017 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport AppKit\n\nclass PreferencesWindowController: NSWindowController {\n    override func awakeFromNib() {\n        super.awakeFromNib()\n\n        window?.title = \"\"\n\n        guard let firstItem = window?.toolbar?.items.first else {\n            return\n        }\n\n        window?.toolbar?.selectedItemIdentifier = firstItem.itemIdentifier\n        selectPane(firstItem)\n    }\n\n    @IBAction func selectPane(_ sender: NSToolbarItem) {\n        switch sender.itemIdentifier.rawValue {\n        case \"general\":\n            contentViewController = GeneralPreferencesViewController()\n        case \"layouts\":\n            contentViewController = LayoutsPreferencesViewController()\n        case \"shortcuts\":\n            contentViewController = ShortcutsPreferencesViewController()\n        case \"mouse\":\n            contentViewController = MousePreferencesViewController()\n        case \"floating\":\n            contentViewController = FloatingPreferencesViewController()\n        case \"debug\":\n            contentViewController = DebugPreferencesViewController()\n        default:\n            break\n        }\n    }\n}\n\nclass PreferencesWindow: NSWindow {\n    @IBOutlet var closeMenuItem: NSMenuItem?\n\n    override func keyDown(with event: NSEvent) {\n        super.keyDown(with: event)\n\n        guard let closeMenuItem = closeMenuItem else {\n            return\n        }\n\n        let eventModifierMask = event.modifierFlags.intersection(.deviceIndependentFlagsMask)\n\n        guard closeMenuItem.keyEquivalentModifierMask == eventModifierMask && closeMenuItem.keyEquivalent == event.characters else {\n            return\n        }\n\n        close()\n    }\n}\n"
  },
  {
    "path": "Amethyst/default.amethyst",
    "content": "{\n    \"layouts\": [\n        \"tall\",\n        \"wide\",\n        \"fullscreen\",\n        \"column\"\n    ],\n    \"mod1\": [\n        \"option\",\n        \"shift\"\n    ],\n    \"mod2\": [\n        \"option\",\n        \"shift\",\n        \"control\"\n    ],\n    \"cycle-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"space\"\n    },\n    \"cycle-layout-backward\": {\n        \"mod\": \"mod2\",\n        \"key\": \"space\"\n    },\n    \"select-tall-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"a\"\n    },\n    \"select-wide-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"s\"\n    },\n    \"select-fullscreen-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"d\"\n    },\n    \"select-column-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"f\"\n    },\n    \"focus-screen-ccw\": {\n        \"mod\": \"mod1\",\n        \"key\": \"p\"\n    },\n    \"focus-screen-cw\": {\n        \"mod\": \"mod1\",\n        \"key\": \"n\"\n    },\n    \"focus-screen-1\": {\n        \"mod\": \"mod1\",\n        \"key\": \"w\"\n    },\n    \"focus-screen-2\": {\n        \"mod\": \"mod1\",\n        \"key\": \"e\"\n    },\n    \"focus-screen-3\": {\n        \"mod\": \"mod1\",\n        \"key\": \"r\"\n    },\n    \"focus-screen-4\": {\n        \"mod\": \"mod1\",\n        \"key\": \"q\"\n    },\n    \"focus-screen-5\": {\n        \"mod\": \"mod1\",\n        \"key\": \"g\"\n    },\n    \"throw-screen-1\": {\n        \"mod\": \"mod2\",\n        \"key\": \"w\"\n    },\n    \"throw-screen-2\": {\n        \"mod\": \"mod2\",\n        \"key\": \"e\"\n    },\n    \"throw-screen-3\": {\n        \"mod\": \"mod2\",\n        \"key\": \"r\"\n    },\n    \"throw-screen-4\": {\n        \"mod\": \"mod2\",\n        \"key\": \"q\"\n    },\n    \"throw-screen-5\": {\n        \"mod\": \"mod2\",\n        \"key\": \"g\"\n    },\n    \"shrink-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \"h\"\n    },\n    \"expand-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \"l\"\n    },\n    \"increase-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \",\"\n    },\n    \"decrease-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \".\"\n    },\n    \"focus-ccw\": {\n        \"mod\": \"mod1\",\n        \"key\": \"j\"\n    },\n    \"focus-cw\": {\n        \"mod\": \"mod1\",\n        \"key\": \"k\"\n    },\n    \"focus-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \"m\"\n    },\n    \"swap-screen-ccw\": {\n        \"mod\": \"mod2\",\n        \"key\": \"h\"\n    },\n    \"swap-screen-cw\": {\n        \"mod\": \"mod2\",\n        \"key\": \"l\"\n    },\n    \"swap-ccw\": {\n        \"mod\": \"mod2\",\n        \"key\": \"j\"\n    },\n    \"swap-cw\": {\n        \"mod\": \"mod2\",\n        \"key\": \"k\"\n    },\n    \"swap-main\": {\n        \"mod\": \"mod1\",\n        \"key\": \"enter\"\n    },\n    \"throw-space-1\": {\n        \"mod\": \"mod2\",\n        \"key\": \"1\"\n    },\n    \"throw-space-2\": {\n        \"mod\": \"mod2\",\n        \"key\": \"2\"\n    },\n    \"throw-space-3\": {\n        \"mod\": \"mod2\",\n        \"key\": \"3\"\n    },\n    \"throw-space-4\": {\n        \"mod\": \"mod2\",\n        \"key\": \"4\"\n    },\n    \"throw-space-5\": {\n        \"mod\": \"mod2\",\n        \"key\": \"5\"\n    },\n    \"throw-space-6\": {\n        \"mod\": \"mod2\",\n        \"key\": \"6\"\n    },\n    \"throw-space-7\": {\n        \"mod\": \"mod2\",\n        \"key\": \"7\"\n    },\n    \"throw-space-8\": {\n        \"mod\": \"mod2\",\n        \"key\": \"8\"\n    },\n    \"throw-space-9\": {\n        \"mod\": \"mod2\",\n        \"key\": \"9\"\n    },\n    \"throw-space-10\": {\n        \"mod\": \"mod2\",\n        \"key\": \"0\"\n    },\n    \"throw-space-left\": {\n        \"mod\": \"mod2\",\n        \"key\": \"left\"\n    },\n    \"throw-space-right\": {\n        \"mod\": \"mod2\",\n        \"key\": \"right\"\n    },\n    \"toggle-float\": {\n        \"mod\": \"mod1\",\n        \"key\": \"t\"\n    },\n    \"toggle-tiling\": {\n        \"mod\": \"mod2\",\n        \"key\": \"t\"\n    },\n    \"enable-tiling\": false,\n    \"disable-tiling\": false,\n    \"display-current-layout\": {\n        \"mod\": \"mod1\",\n        \"key\": \"i\"\n    },\n    \"reevaluate-windows\": {\n        \"mod\": \"mod1\",\n        \"key\": \"z\"\n    },\n    \"toggle-focus-follows-mouse\": {\n        \"mod\": \"mod2\",\n        \"key\": \"x\"\n    },\n    \"relaunch-amethyst\": {\n        \"mod\": \"mod2\",\n        \"key\": \"z\"\n    },\n    \"floating\": [],\n    \"floating-is-blacklist\": true,\n    \"float-small-windows\": true,\n    \"small-window-size\": 500,\n    \"mouse-follows-focus\": false,\n    \"focus-follows-mouse\": false,\n    \"enables-layout-hud\": true,\n    \"enables-layout-hud-on-space-change\": true,\n    \"enables-window-count-hud\": false,\n    \"window-margin-size\": 0,\n    \"window-resize-step\": 5,\n    \"window-margins\": false,\n    \"window-minimum-height\": 0,\n    \"window-minimum-width\": 0,\n    \"window-max-count\": 0,\n    \"ignore-menu-bar\": false,\n    \"use-canary-build\": false,\n    \"new-windows-to-main\": false,\n    \"follow-space-thrown-windows\": true,\n    \"screen-padding-top\": 0,\n    \"screen-padding-bottom\": 0,\n    \"screen-padding-left\": 0,\n    \"screen-padding-right\": 0,\n    \"debug-layout-info\": false,\n    \"restore-layouts-on-launch\": true\n}\n"
  },
  {
    "path": "Amethyst/en.lproj/Credits.rtf",
    "content": "{\\rtf0\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\n{\\colortbl;\\red255\\green255\\blue255;}\n\\paperw9840\\paperh8400\n\\pard\\tx560\\tx1120\\tx1680\\tx2240\\tx2800\\tx3360\\tx3920\\tx4480\\tx5040\\tx5600\\tx6160\\tx6720\\ql\\qnatural\n\n\\f0\\b\\fs24 \\cf0 Engineering:\n\\b0 \\\n\tSome people\\\n\\\n\n\\b Human Interface Design:\n\\b0 \\\n\tSome other people\\\n\\\n\n\\b Testing:\n\\b0 \\\n\tHopefully not nobody\\\n\\\n\n\\b Documentation:\n\\b0 \\\n\tWhoever\\\n\\\n\n\\b With special thanks to:\n\\b0 \\\n\tMom\\\n}\n"
  },
  {
    "path": "Amethyst/en.lproj/InfoPlist.strings",
    "content": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "Amethyst/main.swift",
    "content": "//\n//  main.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/24/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport ArgumentParser\nimport Cocoa\n\nstruct Arguments: ParsableArguments {}\n\nstruct Amethyst: ParsableCommand {\n    static var configuration: CommandConfiguration = CommandConfiguration(\n        subcommands: [Debug.self, App.self],\n        defaultSubcommand: App.self\n    )\n}\n\nstruct App: ParsableCommand {\n    static var configuration: CommandConfiguration = CommandConfiguration(\n        abstract: \"Run the Amethyst application.\"\n    )\n\n    mutating func run() throws {\n        _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)\n    }\n}\n\nif CommandLine.arguments.contains(\"--debug-info\") {\n    print(DebugInfo.description(arguments: CommandLine.arguments))\n} else if CommandLine.arguments.dropFirst().first == \"test\" {\n    _ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)\n} else {\n    do {\n        var command = try Amethyst.parseAsRoot()\n        try command.run()\n    } catch {\n        Arguments.exit(withError: error)\n    }\n    Arguments.exit()\n}\n"
  },
  {
    "path": "Amethyst.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1A4B46EB20AA7717003D5110 /* NSTableView+Amethyst.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4B46EA20AA7717003D5110 /* NSTableView+Amethyst.swift */; };\n\t\t2A6D9A4125E5D24D006A36B5 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A6D9A4025E5D24D006A36B5 /* AppManager.swift */; };\n\t\t4000DB10239CA07000365A0C /* WideLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4000DB0F239CA07000365A0C /* WideLayoutTests.swift */; };\n\t\t400540F22325CDF4004B8656 /* Reliability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400540F12325CDF4004B8656 /* Reliability.swift */; };\n\t\t4006CF881CDFF5F6004CA512 /* NSRunningApplication+Manageable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4006CF871CDFF5F6004CA512 /* NSRunningApplication+Manageable.swift */; };\n\t\t4006CF8E1CDFFE90004CA512 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4006CF8D1CDFFE90004CA512 /* AppDelegate.swift */; };\n\t\t4006CF901CE017BA004CA512 /* UserConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4006CF8F1CE017BA004CA512 /* UserConfiguration.swift */; };\n\t\t400A5E9D2327350C00F0A2C3 /* Space.swift in Sources */ = {isa = PBXBuildFile; fileRef = 400A5E9C2327350C00F0A2C3 /* Space.swift */; };\n\t\t400D48F42A92AF1E0082750F /* Cartography in Frameworks */ = {isa = PBXBuildFile; productRef = 400D48F32A92AF1E0082750F /* Cartography */; };\n\t\t400D48F72A92AF9B0082750F /* LoginServiceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 400D48F62A92AF9B0082750F /* LoginServiceKit */; };\n\t\t400D48FA2A92B0130082750F /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 400D48F92A92B0130082750F /* Sparkle */; };\n\t\t400D48FD2A92B06B0082750F /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 400D48FC2A92B06B0082750F /* SwiftyJSON */; };\n\t\t400D49002A92B0A00082750F /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 400D48FF2A92B0A00082750F /* Yams */; };\n\t\t400F2DF32AABF4FC00C1AAE2 /* KeyboardShortcuts in Frameworks */ = {isa = PBXBuildFile; productRef = 400F2DF22AABF4FC00C1AAE2 /* KeyboardShortcuts */; };\n\t\t40111CC9223370FD003D20BD /* SIWindow+AmethystTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40111CC8223370FD003D20BD /* SIWindow+AmethystTests.swift */; };\n\t\t40111CCB22342CC4003D20BD /* DebugPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40111CCA22342CC4003D20BD /* DebugPreferencesViewController.swift */; };\n\t\t40111CCD22342CF3003D20BD /* DebugPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40111CCC22342CF3003D20BD /* DebugPreferencesViewController.xib */; };\n\t\t401ADBAF2D55A1B6001FF53A /* recommended-main-pane-ratio.js in Resources */ = {isa = PBXBuildFile; fileRef = 401ADBAE2D55A1B6001FF53A /* recommended-main-pane-ratio.js */; };\n\t\t401BBCB62333067F005118F8 /* ColumnLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BBCB52333067F005118F8 /* ColumnLayoutTests.swift */; };\n\t\t401BC8981CE7E45300F89B3F /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8971CE7E45300F89B3F /* WindowManager.swift */; };\n\t\t401BC89A1CE8C6AE00F89B3F /* HotKeyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8991CE8C6AE00F89B3F /* HotKeyManager.swift */; };\n\t\t401BC89C1CE8D58A00F89B3F /* ShortcutsPreferencesListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC89B1CE8D58A00F89B3F /* ShortcutsPreferencesListItemView.swift */; };\n\t\t401BC8A21CE8D86B00F89B3F /* ShortcutsPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8A11CE8D86B00F89B3F /* ShortcutsPreferencesViewController.swift */; };\n\t\t401BC8A41CE8DB0800F89B3F /* GeneralPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8A31CE8DB0800F89B3F /* GeneralPreferencesViewController.swift */; };\n\t\t401BC8A61CE8E65D00F89B3F /* LayoutNameWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8A51CE8E65D00F89B3F /* LayoutNameWindow.swift */; };\n\t\t401BC8A81CE8E94000F89B3F /* WindowsInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8A71CE8E94000F89B3F /* WindowsInformation.swift */; };\n\t\t401BC8AE1CE8FD4700F89B3F /* UserConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8AD1CE8FD4700F89B3F /* UserConfigurationTests.swift */; };\n\t\t401BC8B41CE9250800F89B3F /* HotKeyRegistrar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8B31CE9250800F89B3F /* HotKeyRegistrar.swift */; };\n\t\t401BC8B61CE9259000F89B3F /* LayoutType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8B51CE9259000F89B3F /* LayoutType.swift */; };\n\t\t401BC8BA1CE9319500F89B3F /* FocusFollowsMouseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401BC8B91CE9319500F89B3F /* FocusFollowsMouseManager.swift */; };\n\t\t401C35AF2241BBDD0019ED07 /* ReflowOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C35AE2241BBDD0019ED07 /* ReflowOperation.swift */; };\n\t\t401C35B12244626E0019ED07 /* ApplicationObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C35B02244626E0019ED07 /* ApplicationObservation.swift */; };\n\t\t401C35B32244650F0019ED07 /* MouseState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C35B22244650F0019ED07 /* MouseState.swift */; };\n\t\t401C35B522482EAF0019ED07 /* WindowTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C35B422482EAF0019ED07 /* WindowTransitionCoordinator.swift */; };\n\t\t401C35B7224831470019ED07 /* FocusTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 401C35B6224831470019ED07 /* FocusTransitionCoordinator.swift */; };\n\t\t4029C4F11C112478001E4788 /* Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4029C4F01C112478001E4788 /* Layout.swift */; };\n\t\t402DA3D72F53DCDB00B08CA2 /* Silica in Frameworks */ = {isa = PBXBuildFile; productRef = 402DA3D62F53DCDB00B08CA2 /* Silica */; };\n\t\t402DA3DA2F53DCFE00B08CA2 /* Silica in Frameworks */ = {isa = PBXBuildFile; productRef = 402DA3D92F53DCFE00B08CA2 /* Silica */; };\n\t\t402DB6E21742E41A00D1C936 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 402DB6E11742E41A00D1C936 /* Cocoa.framework */; };\n\t\t402DB6EC1742E41A00D1C936 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 402DB6EA1742E41A00D1C936 /* InfoPlist.strings */; };\n\t\t402DB6F21742E41A00D1C936 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 402DB6F01742E41A00D1C936 /* Credits.rtf */; };\n\t\t402DB6F81742E41A00D1C936 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 402DB6F61742E41A00D1C936 /* MainMenu.xib */; };\n\t\t402DB6FF1742E44E00D1C936 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 402DB6FE1742E44E00D1C936 /* Carbon.framework */; };\n\t\t402F6FA62A81C9E30036B512 /* SkyLight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 402F6FA52A81C9E30036B512 /* SkyLight.framework */; };\n\t\t403E1A2A2337173600DB7B2A /* FloatingLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 403E1A292337173600DB7B2A /* FloatingLayoutTests.swift */; };\n\t\t403E1A2C233719E500DB7B2A /* TallLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 403E1A2B233719E500DB7B2A /* TallLayoutTests.swift */; };\n\t\t4045416F268FFDA000861BE8 /* CustomLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4045416E268FFDA000861BE8 /* CustomLayout.swift */; };\n\t\t404541712697C14A00861BE8 /* CustomLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404541702697C14A00861BE8 /* CustomLayoutTests.swift */; };\n\t\t404541742697C22A00861BE8 /* null.js in Resources */ = {isa = PBXBuildFile; fileRef = 404541732697C22A00861BE8 /* null.js */; };\n\t\t404541762697C62400861BE8 /* TestBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404541752697C62400861BE8 /* TestBundle.swift */; };\n\t\t404541782697CDD000861BE8 /* fullscreen.js in Resources */ = {isa = PBXBuildFile; fileRef = 404541772697CDD000861BE8 /* fullscreen.js */; };\n\t\t4045417A2697EBC500861BE8 /* undefined.js in Resources */ = {isa = PBXBuildFile; fileRef = 404541792697EBC500861BE8 /* undefined.js */; };\n\t\t4045417C2697EE7800861BE8 /* uniform-columns.js in Resources */ = {isa = PBXBuildFile; fileRef = 4045417B2697EE7800861BE8 /* uniform-columns.js */; };\n\t\t4045417E2698030A00861BE8 /* static-ratio-tall.js in Resources */ = {isa = PBXBuildFile; fileRef = 4045417D2698030A00861BE8 /* static-ratio-tall.js */; };\n\t\t4046EFCF2236019400113067 /* Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4046EFCE2236019400113067 /* Window.swift */; };\n\t\t4046EFD12238949900113067 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4046EFD02238949900113067 /* Application.swift */; };\n\t\t404BE9CE1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404BE9CD1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift */; };\n\t\t404BE9D11CFBDF1900D6C537 /* BinarySpacePartitioningLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404BE9D01CFBDF1900D6C537 /* BinarySpacePartitioningLayoutTests.swift */; };\n\t\t40578C19232D9CC0006553A0 /* Screen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40578C18232D9CC0006553A0 /* Screen.swift */; };\n\t\t40578C1B232DC607006553A0 /* TestScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40578C1A232DC607006553A0 /* TestScreen.swift */; };\n\t\t40583E1329AEF70B008602BB /* ApplicationEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40583E1229AEF70B008602BB /* ApplicationEventHandler.swift */; };\n\t\t4058C46F1C4B119500B19D26 /* LayoutNameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4058C46E1C4B119500B19D26 /* LayoutNameWindowController.swift */; };\n\t\t4058C47A1C54113D00B19D26 /* GeneralPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4058C4791C54113D00B19D26 /* GeneralPreferencesViewController.xib */; };\n\t\t4058C47E1C54119B00B19D26 /* ShortcutsPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4058C47D1C54119B00B19D26 /* ShortcutsPreferencesViewController.xib */; };\n\t\t4062AD2B1C1F99EA00DB612B /* FloatingLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD2A1C1F99EA00DB612B /* FloatingLayout.swift */; };\n\t\t4062AD2D1C1F9B8B00DB612B /* FullscreenLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD2C1C1F9B8B00DB612B /* FullscreenLayout.swift */; };\n\t\t4062AD2F1C1F9D6B00DB612B /* TallLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD2E1C1F9D6B00DB612B /* TallLayout.swift */; };\n\t\t4062AD311C1FA29600DB612B /* TallRightLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD301C1FA29600DB612B /* TallRightLayout.swift */; };\n\t\t4062AD331C1FA48C00DB612B /* ColumnLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD321C1FA48C00DB612B /* ColumnLayout.swift */; };\n\t\t4062AD351C1FA62500DB612B /* WideLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD341C1FA62500DB612B /* WideLayout.swift */; };\n\t\t4062AD371C1FA83300DB612B /* RowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD361C1FA83300DB612B /* RowLayout.swift */; };\n\t\t4062AD3B1C206EA900DB612B /* WidescreenTallLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4062AD3A1C206EA900DB612B /* WidescreenTallLayout.swift */; };\n\t\t40637073224EF9B20081299D /* Change.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40637072224EF9B20081299D /* Change.swift */; };\n\t\t40637075224EFED70081299D /* CGInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40637074224EFED70081299D /* CGInfo.swift */; };\n\t\t40637077224F0EFF0081299D /* Screens.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40637076224F0EFF0081299D /* Screens.swift */; };\n\t\t4085824C1EA673AE0075A2C3 /* HotKeyManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4085824B1EA673AE0075A2C3 /* HotKeyManagerTests.swift */; };\n\t\t4087BB042D447F480062A52B /* static-ratio-tall-native-commands.js in Resources */ = {isa = PBXBuildFile; fileRef = 4087BB032D447F480062A52B /* static-ratio-tall-native-commands.js */; };\n\t\t409645B31CEE8D950042F459 /* LogManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409645B21CEE8D950042F459 /* LogManager.swift */; };\n\t\t40A7AEA7232ECF3000E79964 /* Windows.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A7AEA6232ECF3000E79964 /* Windows.swift */; };\n\t\t40A87FDC2404AEAD005EE9C6 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A87FDB2404AEAD005EE9C6 /* main.swift */; };\n\t\t40A87FFD2404B1B4005EE9C6 /* DebugInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A87FFC2404B1B4005EE9C6 /* DebugInfo.swift */; };\n\t\t40A87FFF240AF2BC005EE9C6 /* MousePreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A87FFE240AF2BC005EE9C6 /* MousePreferencesViewController.swift */; };\n\t\t40A88001240AF2D5005EE9C6 /* MousePreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40A88000240AF2D5005EE9C6 /* MousePreferencesViewController.xib */; };\n\t\t40A88003240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40A88002240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift */; };\n\t\t40A88005240C40D0005EE9C6 /* LayoutsPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40A88004240C40D0005EE9C6 /* LayoutsPreferencesViewController.xib */; };\n\t\t40AB5BE923AB0A2B00E29346 /* TallRightLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AB5BE823AB0A2B00E29346 /* TallRightLayoutTests.swift */; };\n\t\t40AB5BEB23AB182300E29346 /* WidescreenTallLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AB5BEA23AB182300E29346 /* WidescreenTallLayoutTests.swift */; };\n\t\t40AB5BED23AC3CF800E29346 /* ThreeColumnLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AB5BEC23AC3CF800E29346 /* ThreeColumnLayoutTests.swift */; };\n\t\t40AE15E32A92E9AF00E14536 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 40AE15E22A92E9AF00E14536 /* SwiftyBeaver */; };\n\t\t40AE15E62A92EA7500E14536 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 40AE15E52A92EA7500E14536 /* RxCocoa */; };\n\t\t40AE15E82A92EA7500E14536 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 40AE15E72A92EA7500E14536 /* RxSwift */; };\n\t\t40AE15EE2A92EBD800E14536 /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = 40AE15ED2A92EBD800E14536 /* Quick */; };\n\t\t40AE15F12A92EC5300E14536 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 40AE15F02A92EC5300E14536 /* Nimble */; };\n\t\t40AE16312A943EF900E14536 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40AE16302A943EF900E14536 /* CoreGraphics.framework */; };\n\t\t40AF71DA20BB348300F58EA9 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 40AF71D920BB348300F58EA9 /* Images.xcassets */; };\n\t\t40AF71DD20BB401400F58EA9 /* FloatingPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40AF71DB20BB401400F58EA9 /* FloatingPreferencesViewController.swift */; };\n\t\t40AF71DE20BB401400F58EA9 /* FloatingPreferencesViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40AF71DC20BB401400F58EA9 /* FloatingPreferencesViewController.xib */; };\n\t\t40B0D145232D2E630021E0A7 /* FullscreenLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B0D144232D2E630021E0A7 /* FullscreenLayoutTests.swift */; };\n\t\t40B0D14B232D944C0021E0A7 /* TestWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B0D14A232D944C0021E0A7 /* TestWindow.swift */; };\n\t\t40B3927C1814967F009A296B /* LayoutNameWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 40B3927B1814967F009A296B /* LayoutNameWindow.xib */; };\n\t\t40C1357C1F202AEE00FF9FA7 /* PreferencesWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40C1357B1F202AEE00FF9FA7 /* PreferencesWindow.swift */; };\n\t\t40C3F91E1BD1B22E00F58660 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C3F91A1BD1B22E00F58660 /* Security.framework */; };\n\t\t40C3F91F1BD1B22E00F58660 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C3F91B1BD1B22E00F58660 /* SystemConfiguration.framework */; };\n\t\t40C3F9231BD1B35E00F58660 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C3F9221BD1B35E00F58660 /* libc++.tbd */; };\n\t\t40C3F9251BD1B36C00F58660 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 40C3F9241BD1B36C00F58660 /* libz.tbd */; };\n\t\t40CEF4301C2B8D21004C3297 /* ScreenManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CEF42F1C2B8D21004C3297 /* ScreenManager.swift */; };\n\t\t40CF37C029B440A100CDB07A /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = 40CF37BF29B440A100CDB07A /* ArgumentParser */; };\n\t\t40CF37C229B58C7400CDB07A /* WindowsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CF37C129B58C7400CDB07A /* WindowsInfo.swift */; };\n\t\t40CF37C429BAC18300CDB07A /* ScreensInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CF37C329BAC18300CDB07A /* ScreensInfo.swift */; };\n\t\t40CF37C629BACD1800CDB07A /* AppsInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40CF37C529BACD1800CDB07A /* AppsInfo.swift */; };\n\t\t40D491D823367590007E0CCB /* RowLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D491D723367590007E0CCB /* RowLayoutTests.swift */; };\n\t\t40D491DB23367630007E0CCB /* FrameAssignmentVerification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40D491DA23367630007E0CCB /* FrameAssignmentVerification.swift */; };\n\t\t40D540F02ABFC7560007F40A /* MASShortcut in Frameworks */ = {isa = PBXBuildFile; productRef = 40D540EF2ABFC7560007F40A /* MASShortcut */; };\n\t\t40D82FF129739C5300F3C18B /* extended.js in Resources */ = {isa = PBXBuildFile; fileRef = 40D82FF029739C5300F3C18B /* extended.js */; };\n\t\t40DA8B6E27D5AA7300C291AF /* subset.js in Resources */ = {isa = PBXBuildFile; fileRef = 40DA8B6D27D5AA7300C291AF /* subset.js */; };\n\t\t40E2D2302D544F1E00D16B87 /* Silica in Frameworks */ = {isa = PBXBuildFile; productRef = 40E2D22F2D544F1E00D16B87 /* Silica */; };\n\t\t40E705D8176EAD7800850DA6 /* default.amethyst in Resources */ = {isa = PBXBuildFile; fileRef = 40E705D7176EAD7800850DA6 /* default.amethyst */; };\n\t\t40EC47F423F3A30100048B4F /* ScreenManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40EC47F323F3A30100048B4F /* ScreenManagerTests.swift */; };\n\t\t40EFFB4A2F0226EF00EDD929 /* Silica in Frameworks */ = {isa = PBXBuildFile; productRef = 40EFFB492F0226EF00EDD929 /* Silica */; };\n\t\t4493EAA22139D9F000AA9623 /* ThreeColumnLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4493EAA12139D9EF00AA9623 /* ThreeColumnLayout.swift */; };\n\t\tAA4AF40D26717DA900D2AE1B /* TwoPaneLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4AF40C26717DA900D2AE1B /* TwoPaneLayout.swift */; };\n\t\tAAAC6BAC2677DF7B00BEC1B0 /* TwoPaneLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAAC6BAB2677DF7B00BEC1B0 /* TwoPaneLayoutTests.swift */; };\n\t\tF0B42E352A3CA45E00298E30 /* TwoPaneRightLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B42E342A3CA45E00298E30 /* TwoPaneRightLayout.swift */; };\n\t\tF46629C4272AD7A30040C275 /* FourColumnLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46629C3272AD7A30040C275 /* FourColumnLayout.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t40D95B4C1C6E2ED800AAF433 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 402DB6D61742E41A00D1C936 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 402DB6DD1742E41A00D1C936;\n\t\t\tremoteInfo = Amethyst;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t1A4B46EA20AA7717003D5110 /* NSTableView+Amethyst.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"NSTableView+Amethyst.swift\"; sourceTree = \"<group>\"; };\n\t\t2A6D9A4025E5D24D006A36B5 /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = \"<group>\"; };\n\t\t4000DB0F239CA07000365A0C /* WideLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WideLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t400540F12325CDF4004B8656 /* Reliability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reliability.swift; sourceTree = \"<group>\"; };\n\t\t4006CF871CDFF5F6004CA512 /* NSRunningApplication+Manageable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"NSRunningApplication+Manageable.swift\"; sourceTree = \"<group>\"; };\n\t\t4006CF8D1CDFFE90004CA512 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t4006CF8F1CE017BA004CA512 /* UserConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserConfiguration.swift; sourceTree = \"<group>\"; };\n\t\t400A5E9C2327350C00F0A2C3 /* Space.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Space.swift; sourceTree = \"<group>\"; };\n\t\t40111CC8223370FD003D20BD /* SIWindow+AmethystTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"SIWindow+AmethystTests.swift\"; sourceTree = \"<group>\"; };\n\t\t40111CCA22342CC4003D20BD /* DebugPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugPreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t40111CCC22342CF3003D20BD /* DebugPreferencesViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DebugPreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t401A529824D3B63A004359A4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t401ADBAE2D55A1B6001FF53A /* recommended-main-pane-ratio.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = \"recommended-main-pane-ratio.js\"; sourceTree = \"<group>\"; };\n\t\t401BBCB52333067F005118F8 /* ColumnLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColumnLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t401BC8971CE7E45300F89B3F /* WindowManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = \"<group>\"; };\n\t\t401BC8991CE8C6AE00F89B3F /* HotKeyManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HotKeyManager.swift; path = ../Events/HotKeyManager.swift; sourceTree = \"<group>\"; };\n\t\t401BC89B1CE8D58A00F89B3F /* ShortcutsPreferencesListItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsPreferencesListItemView.swift; sourceTree = \"<group>\"; };\n\t\t401BC8A11CE8D86B00F89B3F /* ShortcutsPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShortcutsPreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t401BC8A31CE8DB0800F89B3F /* GeneralPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t401BC8A51CE8E65D00F89B3F /* LayoutNameWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutNameWindow.swift; sourceTree = \"<group>\"; };\n\t\t401BC8A71CE8E94000F89B3F /* WindowsInformation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowsInformation.swift; sourceTree = \"<group>\"; };\n\t\t401BC8A91CE8F49200F89B3F /* Amethyst-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"Amethyst-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t401BC8AC1CE8FD4600F89B3F /* AmethystTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"AmethystTests-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t401BC8AD1CE8FD4700F89B3F /* UserConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserConfigurationTests.swift; sourceTree = \"<group>\"; };\n\t\t401BC8B31CE9250800F89B3F /* HotKeyRegistrar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeyRegistrar.swift; sourceTree = \"<group>\"; };\n\t\t401BC8B51CE9259000F89B3F /* LayoutType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutType.swift; sourceTree = \"<group>\"; };\n\t\t401BC8B91CE9319500F89B3F /* FocusFollowsMouseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FocusFollowsMouseManager.swift; sourceTree = \"<group>\"; };\n\t\t401C35AE2241BBDD0019ED07 /* ReflowOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReflowOperation.swift; sourceTree = \"<group>\"; };\n\t\t401C35B02244626E0019ED07 /* ApplicationObservation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationObservation.swift; sourceTree = \"<group>\"; };\n\t\t401C35B22244650F0019ED07 /* MouseState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MouseState.swift; sourceTree = \"<group>\"; };\n\t\t401C35B422482EAF0019ED07 /* WindowTransitionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTransitionCoordinator.swift; sourceTree = \"<group>\"; };\n\t\t401C35B6224831470019ED07 /* FocusTransitionCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusTransitionCoordinator.swift; sourceTree = \"<group>\"; };\n\t\t4029C4F01C112478001E4788 /* Layout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Layout.swift; sourceTree = \"<group>\"; };\n\t\t402DB6DE1742E41A00D1C936 /* Amethyst.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Amethyst.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t402DB6E11742E41A00D1C936 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };\n\t\t402DB6E41742E41A00D1C936 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };\n\t\t402DB6E61742E41A00D1C936 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };\n\t\t402DB6E91742E41A00D1C936 /* Amethyst-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = \"Amethyst-Info.plist\"; sourceTree = \"<group>\"; };\n\t\t402DB6EB1742E41A00D1C936 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = \"<group>\"; };\n\t\t402DB6F11742E41A00D1C936 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = \"<group>\"; };\n\t\t402DB6FE1742E44E00D1C936 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };\n\t\t402F6FA52A81C9E30036B512 /* SkyLight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SkyLight.framework; path = /System/Library/PrivateFrameworks/SkyLight.framework; sourceTree = \"<absolute>\"; };\n\t\t40378E22238F39B900D11E22 /* Amethyst.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Amethyst.entitlements; sourceTree = \"<group>\"; };\n\t\t403E1A292337173600DB7B2A /* FloatingLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t403E1A2B233719E500DB7B2A /* TallLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TallLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t4045416E268FFDA000861BE8 /* CustomLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayout.swift; sourceTree = \"<group>\"; };\n\t\t404541702697C14A00861BE8 /* CustomLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t404541732697C22A00861BE8 /* null.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = null.js; sourceTree = \"<group>\"; };\n\t\t404541752697C62400861BE8 /* TestBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestBundle.swift; sourceTree = \"<group>\"; };\n\t\t404541772697CDD000861BE8 /* fullscreen.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = fullscreen.js; sourceTree = \"<group>\"; };\n\t\t404541792697EBC500861BE8 /* undefined.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = undefined.js; sourceTree = \"<group>\"; };\n\t\t4045417B2697EE7800861BE8 /* uniform-columns.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = \"uniform-columns.js\"; sourceTree = \"<group>\"; };\n\t\t4045417D2698030A00861BE8 /* static-ratio-tall.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = \"static-ratio-tall.js\"; sourceTree = \"<group>\"; };\n\t\t4046EFCE2236019400113067 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = \"<group>\"; };\n\t\t4046EFD02238949900113067 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = \"<group>\"; };\n\t\t404BE9CD1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySpacePartitioningLayout.swift; sourceTree = \"<group>\"; };\n\t\t404BE9D01CFBDF1900D6C537 /* BinarySpacePartitioningLayoutTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BinarySpacePartitioningLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40578C18232D9CC0006553A0 /* Screen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screen.swift; sourceTree = \"<group>\"; };\n\t\t40578C1A232DC607006553A0 /* TestScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestScreen.swift; sourceTree = \"<group>\"; };\n\t\t40583E1229AEF70B008602BB /* ApplicationEventHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationEventHandler.swift; sourceTree = \"<group>\"; };\n\t\t4058C46E1C4B119500B19D26 /* LayoutNameWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LayoutNameWindowController.swift; sourceTree = \"<group>\"; };\n\t\t4058C4791C54113D00B19D26 /* GeneralPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = GeneralPreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t4058C47D1C54119B00B19D26 /* ShortcutsPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShortcutsPreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t4062AD2A1C1F99EA00DB612B /* FloatingLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD2C1C1F9B8B00DB612B /* FullscreenLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullscreenLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD2E1C1F9D6B00DB612B /* TallLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TallLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD301C1FA29600DB612B /* TallRightLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TallRightLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD321C1FA48C00DB612B /* ColumnLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColumnLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD341C1FA62500DB612B /* WideLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WideLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD361C1FA83300DB612B /* RowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowLayout.swift; sourceTree = \"<group>\"; };\n\t\t4062AD3A1C206EA900DB612B /* WidescreenTallLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidescreenTallLayout.swift; sourceTree = \"<group>\"; };\n\t\t40637072224EF9B20081299D /* Change.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Change.swift; sourceTree = \"<group>\"; };\n\t\t40637074224EFED70081299D /* CGInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGInfo.swift; sourceTree = \"<group>\"; };\n\t\t40637076224F0EFF0081299D /* Screens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Screens.swift; sourceTree = \"<group>\"; };\n\t\t4076368C2394BFB500308336 /* AmethystDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AmethystDebug.entitlements; sourceTree = \"<group>\"; };\n\t\t4085824B1EA673AE0075A2C3 /* HotKeyManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeyManagerTests.swift; sourceTree = \"<group>\"; };\n\t\t4087BB032D447F480062A52B /* static-ratio-tall-native-commands.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = \"static-ratio-tall-native-commands.js\"; sourceTree = \"<group>\"; };\n\t\t409645B21CEE8D950042F459 /* LogManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogManager.swift; sourceTree = \"<group>\"; };\n\t\t40A7AEA6232ECF3000E79964 /* Windows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Windows.swift; sourceTree = \"<group>\"; };\n\t\t40A87FDB2404AEAD005EE9C6 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = \"<group>\"; };\n\t\t40A87FFC2404B1B4005EE9C6 /* DebugInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugInfo.swift; sourceTree = \"<group>\"; };\n\t\t40A87FFE240AF2BC005EE9C6 /* MousePreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MousePreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t40A88000240AF2D5005EE9C6 /* MousePreferencesViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MousePreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t40A88002240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutsPreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t40A88004240C40D0005EE9C6 /* LayoutsPreferencesViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = LayoutsPreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t40AB5BE823AB0A2B00E29346 /* TallRightLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TallRightLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40AB5BEA23AB182300E29346 /* WidescreenTallLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidescreenTallLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40AB5BEC23AC3CF800E29346 /* ThreeColumnLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeColumnLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40AE16302A943EF900E14536 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };\n\t\t40AF71D920BB348300F58EA9 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = \"<group>\"; };\n\t\t40AF71DB20BB401400F58EA9 /* FloatingPreferencesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FloatingPreferencesViewController.swift; sourceTree = \"<group>\"; };\n\t\t40AF71DC20BB401400F58EA9 /* FloatingPreferencesViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FloatingPreferencesViewController.xib; sourceTree = \"<group>\"; };\n\t\t40B0D144232D2E630021E0A7 /* FullscreenLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullscreenLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40B0D14A232D944C0021E0A7 /* TestWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestWindow.swift; sourceTree = \"<group>\"; };\n\t\t40B3927B1814967F009A296B /* LayoutNameWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LayoutNameWindow.xib; sourceTree = \"<group>\"; };\n\t\t40C1357B1F202AEE00FF9FA7 /* PreferencesWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindow.swift; sourceTree = \"<group>\"; };\n\t\t40C3F91A1BD1B22E00F58660 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };\n\t\t40C3F91B1BD1B22E00F58660 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };\n\t\t40C3F9221BD1B35E00F58660 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = \"libc++.tbd\"; path = \"usr/lib/libc++.tbd\"; sourceTree = SDKROOT; };\n\t\t40C3F9241BD1B36C00F58660 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };\n\t\t40CEF42F1C2B8D21004C3297 /* ScreenManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenManager.swift; sourceTree = \"<group>\"; usesTabs = 0; };\n\t\t40CF37C129B58C7400CDB07A /* WindowsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowsInfo.swift; sourceTree = \"<group>\"; };\n\t\t40CF37C329BAC18300CDB07A /* ScreensInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreensInfo.swift; sourceTree = \"<group>\"; };\n\t\t40CF37C529BACD1800CDB07A /* AppsInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsInfo.swift; sourceTree = \"<group>\"; };\n\t\t40D491D723367590007E0CCB /* RowLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\t40D491DA23367630007E0CCB /* FrameAssignmentVerification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FrameAssignmentVerification.swift; sourceTree = \"<group>\"; };\n\t\t40D82FF029739C5300F3C18B /* extended.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = extended.js; sourceTree = \"<group>\"; };\n\t\t40D95B471C6E2ED800AAF433 /* AmethystTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AmethystTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t40D95B4B1C6E2ED800AAF433 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t40DA8B6D27D5AA7300C291AF /* subset.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = subset.js; sourceTree = \"<group>\"; };\n\t\t40E705D7176EAD7800850DA6 /* default.amethyst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = default.amethyst; sourceTree = \"<group>\"; };\n\t\t40EC47F323F3A30100048B4F /* ScreenManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenManagerTests.swift; sourceTree = \"<group>\"; };\n\t\t4493EAA12139D9EF00AA9623 /* ThreeColumnLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreeColumnLayout.swift; sourceTree = \"<group>\"; };\n\t\tAA4AF40C26717DA900D2AE1B /* TwoPaneLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoPaneLayout.swift; sourceTree = \"<group>\"; };\n\t\tAAAC6BAB2677DF7B00BEC1B0 /* TwoPaneLayoutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoPaneLayoutTests.swift; sourceTree = \"<group>\"; };\n\t\tF0B42E342A3CA45E00298E30 /* TwoPaneRightLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoPaneRightLayout.swift; sourceTree = \"<group>\"; };\n\t\tF46629C3272AD7A30040C275 /* FourColumnLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FourColumnLayout.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t402DB6DB1742E41A00D1C936 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t40D540F02ABFC7560007F40A /* MASShortcut in Frameworks */,\n\t\t\t\t400D48FD2A92B06B0082750F /* SwiftyJSON in Frameworks */,\n\t\t\t\t40C3F9251BD1B36C00F58660 /* libz.tbd in Frameworks */,\n\t\t\t\t40AE16312A943EF900E14536 /* CoreGraphics.framework in Frameworks */,\n\t\t\t\t40AE15E32A92E9AF00E14536 /* SwiftyBeaver in Frameworks */,\n\t\t\t\t40C3F9231BD1B35E00F58660 /* libc++.tbd in Frameworks */,\n\t\t\t\t402DA3DA2F53DCFE00B08CA2 /* Silica in Frameworks */,\n\t\t\t\t402DB6FF1742E44E00D1C936 /* Carbon.framework in Frameworks */,\n\t\t\t\t402DA3D72F53DCDB00B08CA2 /* Silica in Frameworks */,\n\t\t\t\t400D49002A92B0A00082750F /* Yams in Frameworks */,\n\t\t\t\t40AE15E62A92EA7500E14536 /* RxCocoa in Frameworks */,\n\t\t\t\t40C3F91F1BD1B22E00F58660 /* SystemConfiguration.framework in Frameworks */,\n\t\t\t\t40AE15E82A92EA7500E14536 /* RxSwift in Frameworks */,\n\t\t\t\t400D48F72A92AF9B0082750F /* LoginServiceKit in Frameworks */,\n\t\t\t\t402DB6E21742E41A00D1C936 /* Cocoa.framework in Frameworks */,\n\t\t\t\t400D48F42A92AF1E0082750F /* Cartography in Frameworks */,\n\t\t\t\t40C3F91E1BD1B22E00F58660 /* Security.framework in Frameworks */,\n\t\t\t\t40E2D2302D544F1E00D16B87 /* Silica in Frameworks */,\n\t\t\t\t40CF37C029B440A100CDB07A /* ArgumentParser in Frameworks */,\n\t\t\t\t400D48FA2A92B0130082750F /* Sparkle in Frameworks */,\n\t\t\t\t400F2DF32AABF4FC00C1AAE2 /* KeyboardShortcuts in Frameworks */,\n\t\t\t\t40EFFB4A2F0226EF00EDD929 /* Silica in Frameworks */,\n\t\t\t\t402F6FA62A81C9E30036B512 /* SkyLight.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t40D95B441C6E2ED800AAF433 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t40AE15F12A92EC5300E14536 /* Nimble in Frameworks */,\n\t\t\t\t40AE15EE2A92EBD800E14536 /* Quick in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t4008E724190C4D9D0049E2F6 /* Preferences */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40111CCA22342CC4003D20BD /* DebugPreferencesViewController.swift */,\n\t\t\t\t40111CCC22342CF3003D20BD /* DebugPreferencesViewController.xib */,\n\t\t\t\t40AF71DB20BB401400F58EA9 /* FloatingPreferencesViewController.swift */,\n\t\t\t\t40AF71DC20BB401400F58EA9 /* FloatingPreferencesViewController.xib */,\n\t\t\t\t401BC8A31CE8DB0800F89B3F /* GeneralPreferencesViewController.swift */,\n\t\t\t\t4058C4791C54113D00B19D26 /* GeneralPreferencesViewController.xib */,\n\t\t\t\t40A88002240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift */,\n\t\t\t\t40A88004240C40D0005EE9C6 /* LayoutsPreferencesViewController.xib */,\n\t\t\t\t40A87FFE240AF2BC005EE9C6 /* MousePreferencesViewController.swift */,\n\t\t\t\t40A88000240AF2D5005EE9C6 /* MousePreferencesViewController.xib */,\n\t\t\t\t401BC89B1CE8D58A00F89B3F /* ShortcutsPreferencesListItemView.swift */,\n\t\t\t\t401BC8A11CE8D86B00F89B3F /* ShortcutsPreferencesViewController.swift */,\n\t\t\t\t4058C47D1C54119B00B19D26 /* ShortcutsPreferencesViewController.xib */,\n\t\t\t\t4006CF8F1CE017BA004CA512 /* UserConfiguration.swift */,\n\t\t\t);\n\t\t\tpath = Preferences;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40111CC7223370E1003D20BD /* Categories */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40111CC8223370FD003D20BD /* SIWindow+AmethystTests.swift */,\n\t\t\t);\n\t\t\tpath = Categories;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t401BC8AA1CE8F82700F89B3F /* Managers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2A6D9A4025E5D24D006A36B5 /* AppManager.swift */,\n\t\t\t\t401BC8B91CE9319500F89B3F /* FocusFollowsMouseManager.swift */,\n\t\t\t\t401C35B6224831470019ED07 /* FocusTransitionCoordinator.swift */,\n\t\t\t\t401BC8991CE8C6AE00F89B3F /* HotKeyManager.swift */,\n\t\t\t\t401BC8B31CE9250800F89B3F /* HotKeyRegistrar.swift */,\n\t\t\t\t401BC8B51CE9259000F89B3F /* LayoutType.swift */,\n\t\t\t\t409645B21CEE8D950042F459 /* LogManager.swift */,\n\t\t\t\t40CEF42F1C2B8D21004C3297 /* ScreenManager.swift */,\n\t\t\t\t40637076224F0EFF0081299D /* Screens.swift */,\n\t\t\t\t401BC8971CE7E45300F89B3F /* WindowManager.swift */,\n\t\t\t\t40A7AEA6232ECF3000E79964 /* Windows.swift */,\n\t\t\t\t401C35B422482EAF0019ED07 /* WindowTransitionCoordinator.swift */,\n\t\t\t);\n\t\t\tpath = Managers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t401BC8AB1CE8F84500F89B3F /* Preferences */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t401BC8AD1CE8FD4700F89B3F /* UserConfigurationTests.swift */,\n\t\t\t);\n\t\t\tname = Preferences;\n\t\t\tpath = Configuration;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t401C35AD2241BA760019ED07 /* Layouts */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t404BE9CD1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift */,\n\t\t\t\t4062AD321C1FA48C00DB612B /* ColumnLayout.swift */,\n\t\t\t\t4062AD2A1C1F99EA00DB612B /* FloatingLayout.swift */,\n\t\t\t\t4062AD2C1C1F9B8B00DB612B /* FullscreenLayout.swift */,\n\t\t\t\t4062AD361C1FA83300DB612B /* RowLayout.swift */,\n\t\t\t\t4062AD2E1C1F9D6B00DB612B /* TallLayout.swift */,\n\t\t\t\tAA4AF40C26717DA900D2AE1B /* TwoPaneLayout.swift */,\n\t\t\t\t4062AD301C1FA29600DB612B /* TallRightLayout.swift */,\n\t\t\t\t4493EAA12139D9EF00AA9623 /* ThreeColumnLayout.swift */,\n\t\t\t\tF46629C3272AD7A30040C275 /* FourColumnLayout.swift */,\n\t\t\t\t4062AD341C1FA62500DB612B /* WideLayout.swift */,\n\t\t\t\t4062AD3A1C206EA900DB612B /* WidescreenTallLayout.swift */,\n\t\t\t\t4045416E268FFDA000861BE8 /* CustomLayout.swift */,\n\t\t\t\tF0B42E342A3CA45E00298E30 /* TwoPaneRightLayout.swift */,\n\t\t\t);\n\t\t\tpath = Layouts;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6D51742E41A00D1C936 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t402DB6E71742E41A00D1C936 /* Amethyst */,\n\t\t\t\t40D95B481C6E2ED800AAF433 /* AmethystTests */,\n\t\t\t\t402DB6E01742E41A00D1C936 /* Frameworks */,\n\t\t\t\t402DB6DF1742E41A00D1C936 /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t\tusesTabs = 0;\n\t\t};\n\t\t402DB6DF1742E41A00D1C936 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t402DB6DE1742E41A00D1C936 /* Amethyst.app */,\n\t\t\t\t40D95B471C6E2ED800AAF433 /* AmethystTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6E01742E41A00D1C936 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40AE16302A943EF900E14536 /* CoreGraphics.framework */,\n\t\t\t\t402F6FA52A81C9E30036B512 /* SkyLight.framework */,\n\t\t\t\t40C3F9241BD1B36C00F58660 /* libz.tbd */,\n\t\t\t\t40C3F9221BD1B35E00F58660 /* libc++.tbd */,\n\t\t\t\t40C3F91A1BD1B22E00F58660 /* Security.framework */,\n\t\t\t\t40C3F91B1BD1B22E00F58660 /* SystemConfiguration.framework */,\n\t\t\t\t402DB6FE1742E44E00D1C936 /* Carbon.framework */,\n\t\t\t\t402DB6E11742E41A00D1C936 /* Cocoa.framework */,\n\t\t\t\t402DB6E31742E41A00D1C936 /* Other Frameworks */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6E31742E41A00D1C936 /* Other Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t402DB6E41742E41A00D1C936 /* AppKit.framework */,\n\t\t\t\t402DB6E61742E41A00D1C936 /* Foundation.framework */,\n\t\t\t);\n\t\t\tname = \"Other Frameworks\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6E71742E41A00D1C936 /* Amethyst */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4076368C2394BFB500308336 /* AmethystDebug.entitlements */,\n\t\t\t\t40378E22238F39B900D11E22 /* Amethyst.entitlements */,\n\t\t\t\t40A87FFB2404B194005EE9C6 /* Debug */,\n\t\t\t\t4046EFCD2236018400113067 /* Model */,\n\t\t\t\t401BC8AA1CE8F82700F89B3F /* Managers */,\n\t\t\t\t4008E724190C4D9D0049E2F6 /* Preferences */,\n\t\t\t\t40B392831814977D009A296B /* View */,\n\t\t\t\t40B337E51745D1600001D8FC /* Categories */,\n\t\t\t\t40B337E11745CE340001D8FC /* Layout */,\n\t\t\t\t4006CF8D1CDFFE90004CA512 /* AppDelegate.swift */,\n\t\t\t\t402DB6F61742E41A00D1C936 /* MainMenu.xib */,\n\t\t\t\t40AF71D920BB348300F58EA9 /* Images.xcassets */,\n\t\t\t\t402DB6E81742E41A00D1C936 /* Supporting Files */,\n\t\t\t);\n\t\t\tpath = Amethyst;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6E81742E41A00D1C936 /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t401BC8A91CE8F49200F89B3F /* Amethyst-Bridging-Header.h */,\n\t\t\t\t402DB6E91742E41A00D1C936 /* Amethyst-Info.plist */,\n\t\t\t\t402DB6EA1742E41A00D1C936 /* InfoPlist.strings */,\n\t\t\t\t402DB6F01742E41A00D1C936 /* Credits.rtf */,\n\t\t\t\t40E705D7176EAD7800850DA6 /* default.amethyst */,\n\t\t\t\t40A87FDB2404AEAD005EE9C6 /* main.swift */,\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t404541722697C16B00861BE8 /* CustomLayouts */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40D82FF029739C5300F3C18B /* extended.js */,\n\t\t\t\t404541772697CDD000861BE8 /* fullscreen.js */,\n\t\t\t\t404541732697C22A00861BE8 /* null.js */,\n\t\t\t\t401ADBAE2D55A1B6001FF53A /* recommended-main-pane-ratio.js */,\n\t\t\t\t4045417D2698030A00861BE8 /* static-ratio-tall.js */,\n\t\t\t\t4087BB032D447F480062A52B /* static-ratio-tall-native-commands.js */,\n\t\t\t\t40DA8B6D27D5AA7300C291AF /* subset.js */,\n\t\t\t\t404541792697EBC500861BE8 /* undefined.js */,\n\t\t\t\t4045417B2697EE7800861BE8 /* uniform-columns.js */,\n\t\t\t);\n\t\t\tpath = CustomLayouts;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t4046EFCD2236018400113067 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4046EFD02238949900113067 /* Application.swift */,\n\t\t\t\t401C35B02244626E0019ED07 /* ApplicationObservation.swift */,\n\t\t\t\t40637074224EFED70081299D /* CGInfo.swift */,\n\t\t\t\t40637072224EF9B20081299D /* Change.swift */,\n\t\t\t\t401C35B22244650F0019ED07 /* MouseState.swift */,\n\t\t\t\t400540F12325CDF4004B8656 /* Reliability.swift */,\n\t\t\t\t40578C18232D9CC0006553A0 /* Screen.swift */,\n\t\t\t\t400A5E9C2327350C00F0A2C3 /* Space.swift */,\n\t\t\t\t4046EFCE2236019400113067 /* Window.swift */,\n\t\t\t\t401BC8A71CE8E94000F89B3F /* WindowsInformation.swift */,\n\t\t\t\t40583E1229AEF70B008602BB /* ApplicationEventHandler.swift */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t404BE9CF1CFBDEFD00D6C537 /* Layout */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t404BE9D01CFBDF1900D6C537 /* BinarySpacePartitioningLayoutTests.swift */,\n\t\t\t\t401BBCB52333067F005118F8 /* ColumnLayoutTests.swift */,\n\t\t\t\t404541702697C14A00861BE8 /* CustomLayoutTests.swift */,\n\t\t\t\t403E1A292337173600DB7B2A /* FloatingLayoutTests.swift */,\n\t\t\t\t40B0D144232D2E630021E0A7 /* FullscreenLayoutTests.swift */,\n\t\t\t\t40D491D723367590007E0CCB /* RowLayoutTests.swift */,\n\t\t\t\t403E1A2B233719E500DB7B2A /* TallLayoutTests.swift */,\n\t\t\t\t40AB5BE823AB0A2B00E29346 /* TallRightLayoutTests.swift */,\n\t\t\t\t40AB5BEC23AC3CF800E29346 /* ThreeColumnLayoutTests.swift */,\n\t\t\t\tAAAC6BAB2677DF7B00BEC1B0 /* TwoPaneLayoutTests.swift */,\n\t\t\t\t4000DB0F239CA07000365A0C /* WideLayoutTests.swift */,\n\t\t\t\t40AB5BEA23AB182300E29346 /* WidescreenTallLayoutTests.swift */,\n\t\t\t);\n\t\t\tpath = Layout;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t4085824A1EA673950075A2C3 /* Managers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4085824B1EA673AE0075A2C3 /* HotKeyManagerTests.swift */,\n\t\t\t\t40EC47F323F3A30100048B4F /* ScreenManagerTests.swift */,\n\t\t\t);\n\t\t\tpath = Managers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40A87FFB2404B194005EE9C6 /* Debug */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40A87FFC2404B1B4005EE9C6 /* DebugInfo.swift */,\n\t\t\t\t40CF37C129B58C7400CDB07A /* WindowsInfo.swift */,\n\t\t\t\t40CF37C329BAC18300CDB07A /* ScreensInfo.swift */,\n\t\t\t\t40CF37C529BACD1800CDB07A /* AppsInfo.swift */,\n\t\t\t);\n\t\t\tpath = Debug;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40B0D148232D942B0021E0A7 /* Tests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4085824A1EA673950075A2C3 /* Managers */,\n\t\t\t\t404BE9CF1CFBDEFD00D6C537 /* Layout */,\n\t\t\t\t401BC8AB1CE8F84500F89B3F /* Preferences */,\n\t\t\t\t40111CC7223370E1003D20BD /* Categories */,\n\t\t\t);\n\t\t\tpath = Tests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40B0D149232D94410021E0A7 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t404541722697C16B00861BE8 /* CustomLayouts */,\n\t\t\t\t40578C1A232DC607006553A0 /* TestScreen.swift */,\n\t\t\t\t40B0D14A232D944C0021E0A7 /* TestWindow.swift */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40B337E11745CE340001D8FC /* Layout */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t401C35AD2241BA760019ED07 /* Layouts */,\n\t\t\t\t4029C4F01C112478001E4788 /* Layout.swift */,\n\t\t\t\t401C35AE2241BBDD0019ED07 /* ReflowOperation.swift */,\n\t\t\t);\n\t\t\tpath = Layout;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40B337E51745D1600001D8FC /* Categories */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4006CF871CDFF5F6004CA512 /* NSRunningApplication+Manageable.swift */,\n\t\t\t\t1A4B46EA20AA7717003D5110 /* NSTableView+Amethyst.swift */,\n\t\t\t);\n\t\t\tpath = Categories;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40B392831814977D009A296B /* View */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40B3927B1814967F009A296B /* LayoutNameWindow.xib */,\n\t\t\t\t401BC8A51CE8E65D00F89B3F /* LayoutNameWindow.swift */,\n\t\t\t\t4058C46E1C4B119500B19D26 /* LayoutNameWindowController.swift */,\n\t\t\t\t40C1357B1F202AEE00FF9FA7 /* PreferencesWindow.swift */,\n\t\t\t);\n\t\t\tpath = View;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40D491D9233675E3007E0CCB /* Helpers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40D491DA23367630007E0CCB /* FrameAssignmentVerification.swift */,\n\t\t\t\t404541752697C62400861BE8 /* TestBundle.swift */,\n\t\t\t);\n\t\t\tpath = Helpers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t40D95B481C6E2ED800AAF433 /* AmethystTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t40D491D9233675E3007E0CCB /* Helpers */,\n\t\t\t\t40B0D149232D94410021E0A7 /* Model */,\n\t\t\t\t40B0D148232D942B0021E0A7 /* Tests */,\n\t\t\t\t40D95B4B1C6E2ED800AAF433 /* Info.plist */,\n\t\t\t\t401BC8AC1CE8FD4600F89B3F /* AmethystTests-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = AmethystTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t402DB6DD1742E41A00D1C936 /* Amethyst */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 402DB6FB1742E41A00D1C936 /* Build configuration list for PBXNativeTarget \"Amethyst\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t4058C4701C4C4F9E00B19D26 /* Lint */,\n\t\t\t\t402DB6DA1742E41A00D1C936 /* Sources */,\n\t\t\t\t402DB6DB1742E41A00D1C936 /* Frameworks */,\n\t\t\t\t402DB6DC1742E41A00D1C936 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Amethyst;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t40CF37BF29B440A100CDB07A /* ArgumentParser */,\n\t\t\t\t400D48F32A92AF1E0082750F /* Cartography */,\n\t\t\t\t400D48F62A92AF9B0082750F /* LoginServiceKit */,\n\t\t\t\t400D48F92A92B0130082750F /* Sparkle */,\n\t\t\t\t400D48FC2A92B06B0082750F /* SwiftyJSON */,\n\t\t\t\t400D48FF2A92B0A00082750F /* Yams */,\n\t\t\t\t40AE15E22A92E9AF00E14536 /* SwiftyBeaver */,\n\t\t\t\t40AE15E52A92EA7500E14536 /* RxCocoa */,\n\t\t\t\t40AE15E72A92EA7500E14536 /* RxSwift */,\n\t\t\t\t400F2DF22AABF4FC00C1AAE2 /* KeyboardShortcuts */,\n\t\t\t\t40D540EF2ABFC7560007F40A /* MASShortcut */,\n\t\t\t\t40E2D22F2D544F1E00D16B87 /* Silica */,\n\t\t\t\t40EFFB492F0226EF00EDD929 /* Silica */,\n\t\t\t\t402DA3D62F53DCDB00B08CA2 /* Silica */,\n\t\t\t\t402DA3D92F53DCFE00B08CA2 /* Silica */,\n\t\t\t);\n\t\t\tproductName = Amethyst;\n\t\t\tproductReference = 402DB6DE1742E41A00D1C936 /* Amethyst.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t40D95B461C6E2ED800AAF433 /* AmethystTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 40D95B4E1C6E2ED800AAF433 /* Build configuration list for PBXNativeTarget \"AmethystTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t40D95B431C6E2ED800AAF433 /* Sources */,\n\t\t\t\t40D95B441C6E2ED800AAF433 /* Frameworks */,\n\t\t\t\t40D95B451C6E2ED800AAF433 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t40D95B4D1C6E2ED800AAF433 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = AmethystTests;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t40AE15ED2A92EBD800E14536 /* Quick */,\n\t\t\t\t40AE15F02A92EC5300E14536 /* Nimble */,\n\t\t\t);\n\t\t\tproductName = AmethystTests;\n\t\t\tproductReference = 40D95B471C6E2ED800AAF433 /* AmethystTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t402DB6D61742E41A00D1C936 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tCLASSPREFIX = AM;\n\t\t\t\tLastSwiftUpdateCheck = 0730;\n\t\t\t\tLastTestingUpgradeCheck = 0700;\n\t\t\t\tLastUpgradeCheck = 1500;\n\t\t\t\tORGANIZATIONNAME = \"Ian Ynda-Hummel\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t402DB6DD1742E41A00D1C936 = {\n\t\t\t\t\t\tLastSwiftMigration = 1010;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t\t40D95B461C6E2ED800AAF433 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.2.1;\n\t\t\t\t\t\tLastSwiftMigration = 1010;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tTestTargetID = 402DB6DD1742E41A00D1C936;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 402DB6D91742E41A00D1C936 /* Build configuration list for PBXProject \"Amethyst\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 402DB6D51742E41A00D1C936;\n\t\t\tpackageReferences = (\n\t\t\t\t40CF37BE29B440A100CDB07A /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */,\n\t\t\t\t400D48F22A92AF1E0082750F /* XCRemoteSwiftPackageReference \"Cartography\" */,\n\t\t\t\t400D48F52A92AF9B0082750F /* XCRemoteSwiftPackageReference \"LoginServiceKit\" */,\n\t\t\t\t400D48F82A92B0130082750F /* XCRemoteSwiftPackageReference \"Sparkle\" */,\n\t\t\t\t400D48FB2A92B06A0082750F /* XCRemoteSwiftPackageReference \"SwiftyJSON\" */,\n\t\t\t\t400D48FE2A92B0A00082750F /* XCRemoteSwiftPackageReference \"Yams\" */,\n\t\t\t\t40AE15E12A92E9AF00E14536 /* XCRemoteSwiftPackageReference \"SwiftyBeaver\" */,\n\t\t\t\t40AE15E42A92EA7400E14536 /* XCRemoteSwiftPackageReference \"RxSwift\" */,\n\t\t\t\t40AE15EC2A92EBD800E14536 /* XCRemoteSwiftPackageReference \"Quick\" */,\n\t\t\t\t40AE15EF2A92EC5300E14536 /* XCRemoteSwiftPackageReference \"Nimble\" */,\n\t\t\t\t400F2DF12AABF4FC00C1AAE2 /* XCRemoteSwiftPackageReference \"KeyboardShortcuts\" */,\n\t\t\t\t40D540EE2ABFC7560007F40A /* XCRemoteSwiftPackageReference \"MASShortcut\" */,\n\t\t\t\t402DA3D82F53DCFE00B08CA2 /* XCRemoteSwiftPackageReference \"silica\" */,\n\t\t\t);\n\t\t\tproductRefGroup = 402DB6DF1742E41A00D1C936 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t402DB6DD1742E41A00D1C936 /* Amethyst */,\n\t\t\t\t40D95B461C6E2ED800AAF433 /* AmethystTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t402DB6DC1742E41A00D1C936 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4058C47A1C54113D00B19D26 /* GeneralPreferencesViewController.xib in Resources */,\n\t\t\t\t40111CCD22342CF3003D20BD /* DebugPreferencesViewController.xib in Resources */,\n\t\t\t\t40A88001240AF2D5005EE9C6 /* MousePreferencesViewController.xib in Resources */,\n\t\t\t\t402DB6EC1742E41A00D1C936 /* InfoPlist.strings in Resources */,\n\t\t\t\t402DB6F21742E41A00D1C936 /* Credits.rtf in Resources */,\n\t\t\t\t40AF71DA20BB348300F58EA9 /* Images.xcassets in Resources */,\n\t\t\t\t402DB6F81742E41A00D1C936 /* MainMenu.xib in Resources */,\n\t\t\t\t4058C47E1C54119B00B19D26 /* ShortcutsPreferencesViewController.xib in Resources */,\n\t\t\t\t40B3927C1814967F009A296B /* LayoutNameWindow.xib in Resources */,\n\t\t\t\t40AF71DE20BB401400F58EA9 /* FloatingPreferencesViewController.xib in Resources */,\n\t\t\t\t40E705D8176EAD7800850DA6 /* default.amethyst in Resources */,\n\t\t\t\t40A88005240C40D0005EE9C6 /* LayoutsPreferencesViewController.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t40D95B451C6E2ED800AAF433 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t4087BB042D447F480062A52B /* static-ratio-tall-native-commands.js in Resources */,\n\t\t\t\t4045417E2698030A00861BE8 /* static-ratio-tall.js in Resources */,\n\t\t\t\t404541742697C22A00861BE8 /* null.js in Resources */,\n\t\t\t\t4045417A2697EBC500861BE8 /* undefined.js in Resources */,\n\t\t\t\t4045417C2697EE7800861BE8 /* uniform-columns.js in Resources */,\n\t\t\t\t40DA8B6E27D5AA7300C291AF /* subset.js in Resources */,\n\t\t\t\t40D82FF129739C5300F3C18B /* extended.js in Resources */,\n\t\t\t\t401ADBAF2D55A1B6001FF53A /* recommended-main-pane-ratio.js in Resources */,\n\t\t\t\t404541782697CDD000861BE8 /* fullscreen.js in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t4058C4701C4C4F9E00B19D26 /* Lint */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = Lint;\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"export PATH=\\\"$PATH:/opt/homebrew/bin\\\"\\nswiftlint --fix\\nswiftlint\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t402DB6DA1742E41A00D1C936 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t40CF37C229B58C7400CDB07A /* WindowsInfo.swift in Sources */,\n\t\t\t\t40AF71DD20BB401400F58EA9 /* FloatingPreferencesViewController.swift in Sources */,\n\t\t\t\t40A88003240C40BC005EE9C6 /* LayoutsPreferencesViewController.swift in Sources */,\n\t\t\t\t4046EFD12238949900113067 /* Application.swift in Sources */,\n\t\t\t\t4062AD331C1FA48C00DB612B /* ColumnLayout.swift in Sources */,\n\t\t\t\t409645B31CEE8D950042F459 /* LogManager.swift in Sources */,\n\t\t\t\t4006CF881CDFF5F6004CA512 /* NSRunningApplication+Manageable.swift in Sources */,\n\t\t\t\t401BC89A1CE8C6AE00F89B3F /* HotKeyManager.swift in Sources */,\n\t\t\t\t4062AD2F1C1F9D6B00DB612B /* TallLayout.swift in Sources */,\n\t\t\t\t40578C19232D9CC0006553A0 /* Screen.swift in Sources */,\n\t\t\t\t401BC89C1CE8D58A00F89B3F /* ShortcutsPreferencesListItemView.swift in Sources */,\n\t\t\t\t400540F22325CDF4004B8656 /* Reliability.swift in Sources */,\n\t\t\t\t4029C4F11C112478001E4788 /* Layout.swift in Sources */,\n\t\t\t\tAA4AF40D26717DA900D2AE1B /* TwoPaneLayout.swift in Sources */,\n\t\t\t\t401BC8A41CE8DB0800F89B3F /* GeneralPreferencesViewController.swift in Sources */,\n\t\t\t\t2A6D9A4125E5D24D006A36B5 /* AppManager.swift in Sources */,\n\t\t\t\t4046EFCF2236019400113067 /* Window.swift in Sources */,\n\t\t\t\t40A87FDC2404AEAD005EE9C6 /* main.swift in Sources */,\n\t\t\t\tF46629C4272AD7A30040C275 /* FourColumnLayout.swift in Sources */,\n\t\t\t\t401BC8A61CE8E65D00F89B3F /* LayoutNameWindow.swift in Sources */,\n\t\t\t\t401C35AF2241BBDD0019ED07 /* ReflowOperation.swift in Sources */,\n\t\t\t\t401BC8B41CE9250800F89B3F /* HotKeyRegistrar.swift in Sources */,\n\t\t\t\t40637073224EF9B20081299D /* Change.swift in Sources */,\n\t\t\t\t40CEF4301C2B8D21004C3297 /* ScreenManager.swift in Sources */,\n\t\t\t\t401C35B522482EAF0019ED07 /* WindowTransitionCoordinator.swift in Sources */,\n\t\t\t\t40CF37C629BACD1800CDB07A /* AppsInfo.swift in Sources */,\n\t\t\t\t40A87FFD2404B1B4005EE9C6 /* DebugInfo.swift in Sources */,\n\t\t\t\t4493EAA22139D9F000AA9623 /* ThreeColumnLayout.swift in Sources */,\n\t\t\t\t40A7AEA7232ECF3000E79964 /* Windows.swift in Sources */,\n\t\t\t\tF0B42E352A3CA45E00298E30 /* TwoPaneRightLayout.swift in Sources */,\n\t\t\t\t404BE9CE1CFBB6E900D6C537 /* BinarySpacePartitioningLayout.swift in Sources */,\n\t\t\t\t4062AD3B1C206EA900DB612B /* WidescreenTallLayout.swift in Sources */,\n\t\t\t\t4058C46F1C4B119500B19D26 /* LayoutNameWindowController.swift in Sources */,\n\t\t\t\t4062AD311C1FA29600DB612B /* TallRightLayout.swift in Sources */,\n\t\t\t\t401BC8B61CE9259000F89B3F /* LayoutType.swift in Sources */,\n\t\t\t\t401C35B12244626E0019ED07 /* ApplicationObservation.swift in Sources */,\n\t\t\t\t401C35B7224831470019ED07 /* FocusTransitionCoordinator.swift in Sources */,\n\t\t\t\t40CF37C429BAC18300CDB07A /* ScreensInfo.swift in Sources */,\n\t\t\t\t4062AD371C1FA83300DB612B /* RowLayout.swift in Sources */,\n\t\t\t\t4006CF8E1CDFFE90004CA512 /* AppDelegate.swift in Sources */,\n\t\t\t\t4006CF901CE017BA004CA512 /* UserConfiguration.swift in Sources */,\n\t\t\t\t4062AD2B1C1F99EA00DB612B /* FloatingLayout.swift in Sources */,\n\t\t\t\t401BC8A21CE8D86B00F89B3F /* ShortcutsPreferencesViewController.swift in Sources */,\n\t\t\t\t401BC8A81CE8E94000F89B3F /* WindowsInformation.swift in Sources */,\n\t\t\t\t401BC8981CE7E45300F89B3F /* WindowManager.swift in Sources */,\n\t\t\t\t400A5E9D2327350C00F0A2C3 /* Space.swift in Sources */,\n\t\t\t\t401BC8BA1CE9319500F89B3F /* FocusFollowsMouseManager.swift in Sources */,\n\t\t\t\t40C1357C1F202AEE00FF9FA7 /* PreferencesWindow.swift in Sources */,\n\t\t\t\t40583E1329AEF70B008602BB /* ApplicationEventHandler.swift in Sources */,\n\t\t\t\t4062AD351C1FA62500DB612B /* WideLayout.swift in Sources */,\n\t\t\t\t40A87FFF240AF2BC005EE9C6 /* MousePreferencesViewController.swift in Sources */,\n\t\t\t\t40111CCB22342CC4003D20BD /* DebugPreferencesViewController.swift in Sources */,\n\t\t\t\t401C35B32244650F0019ED07 /* MouseState.swift in Sources */,\n\t\t\t\t4062AD2D1C1F9B8B00DB612B /* FullscreenLayout.swift in Sources */,\n\t\t\t\t40637075224EFED70081299D /* CGInfo.swift in Sources */,\n\t\t\t\t1A4B46EB20AA7717003D5110 /* NSTableView+Amethyst.swift in Sources */,\n\t\t\t\t4045416F268FFDA000861BE8 /* CustomLayout.swift in Sources */,\n\t\t\t\t40637077224F0EFF0081299D /* Screens.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t40D95B431C6E2ED800AAF433 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t403E1A2A2337173600DB7B2A /* FloatingLayoutTests.swift in Sources */,\n\t\t\t\t40111CC9223370FD003D20BD /* SIWindow+AmethystTests.swift in Sources */,\n\t\t\t\t401BBCB62333067F005118F8 /* ColumnLayoutTests.swift in Sources */,\n\t\t\t\t401BC8AE1CE8FD4700F89B3F /* UserConfigurationTests.swift in Sources */,\n\t\t\t\tAAAC6BAC2677DF7B00BEC1B0 /* TwoPaneLayoutTests.swift in Sources */,\n\t\t\t\t404BE9D11CFBDF1900D6C537 /* BinarySpacePartitioningLayoutTests.swift in Sources */,\n\t\t\t\t4085824C1EA673AE0075A2C3 /* HotKeyManagerTests.swift in Sources */,\n\t\t\t\t40578C1B232DC607006553A0 /* TestScreen.swift in Sources */,\n\t\t\t\t40B0D145232D2E630021E0A7 /* FullscreenLayoutTests.swift in Sources */,\n\t\t\t\t404541762697C62400861BE8 /* TestBundle.swift in Sources */,\n\t\t\t\t40B0D14B232D944C0021E0A7 /* TestWindow.swift in Sources */,\n\t\t\t\t40D491D823367590007E0CCB /* RowLayoutTests.swift in Sources */,\n\t\t\t\t403E1A2C233719E500DB7B2A /* TallLayoutTests.swift in Sources */,\n\t\t\t\t404541712697C14A00861BE8 /* CustomLayoutTests.swift in Sources */,\n\t\t\t\t4000DB10239CA07000365A0C /* WideLayoutTests.swift in Sources */,\n\t\t\t\t40AB5BEB23AB182300E29346 /* WidescreenTallLayoutTests.swift in Sources */,\n\t\t\t\t40EC47F423F3A30100048B4F /* ScreenManagerTests.swift in Sources */,\n\t\t\t\t40D491DB23367630007E0CCB /* FrameAssignmentVerification.swift in Sources */,\n\t\t\t\t40AB5BED23AC3CF800E29346 /* ThreeColumnLayoutTests.swift in Sources */,\n\t\t\t\t40AB5BE923AB0A2B00E29346 /* TallRightLayoutTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t40D95B4D1C6E2ED800AAF433 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 402DB6DD1742E41A00D1C936 /* Amethyst */;\n\t\t\ttargetProxy = 40D95B4C1C6E2ED800AAF433 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t402DB6EA1742E41A00D1C936 /* InfoPlist.strings */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t402DB6EB1742E41A00D1C936 /* en */,\n\t\t\t);\n\t\t\tname = InfoPlist.strings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6F01742E41A00D1C936 /* Credits.rtf */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t402DB6F11742E41A00D1C936 /* en */,\n\t\t\t);\n\t\t\tname = Credits.rtf;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t402DB6F61742E41A00D1C936 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t401A529824D3B63A004359A4 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t402DB6F91742E41A00D1C936 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tENABLE_MODULE_VERIFIER = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_SYMBOLS_PRIVATE_EXTERN = NO;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++14\";\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSTRIP_INSTALLED_PRODUCT = NO;\n\t\t\t\tSTRIP_SWIFT_SYMBOLS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t402DB6FA1742E41A00D1C936 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tENABLE_MODULE_VERIFIER = YES;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_ENABLE_OBJC_EXCEPTIONS = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = \"RELEASE=1\";\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++14\";\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSTRIP_INSTALLED_PRODUCT = NO;\n\t\t\t\tSTRIP_SWIFT_SYMBOLS = NO;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t402DB6FC1742E41A00D1C936 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Amethyst/AmethystDebug.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"-\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 128;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_MODULE_VERIFIER = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/Amethyst-hayuzxrdmhaqmverzffhmryaexfi/Build/Products/Debug\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t\t\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\",\n\t\t\t\t);\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = NO;\n\t\t\t\tGCC_PREFIX_HEADER = \"\";\n\t\t\t\tINFOPLIST_FILE = \"Amethyst/Amethyst-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11;\n\t\t\t\tMARKETING_VERSION = 0.24.2;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++14\";\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited) \\\"-D\\\" \\\"DEBUG\\\"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.amethyst.${PRODUCT_NAME:rfc1034identifier}\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tWRAPPER_EXTENSION = app;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t402DB6FD1742E41A00D1C936 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Amethyst/Amethyst.entitlements;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Apple Development\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 128;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 82P2XLB4UH;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_MODULE_VERIFIER = YES;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/Amethyst-hayuzxrdmhaqmverzffhmryaexfi/Build/Products/Debug\",\n\t\t\t\t\t\"$(PROJECT_DIR)\",\n\t\t\t\t\t\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\",\n\t\t\t\t);\n\t\t\t\tGCC_PRECOMPILE_PREFIX_HEADER = NO;\n\t\t\t\tGCC_PREFIX_HEADER = \"\";\n\t\t\t\tGCC_SYMBOLS_PRIVATE_EXTERN = NO;\n\t\t\t\tINFOPLIST_FILE = \"Amethyst/Amethyst-Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11;\n\t\t\t\tMARKETING_VERSION = 0.24.2;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++14\";\n\t\t\t\tONLY_ACTIVE_ARCH = NO;\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited) \\\"-D\\\" \\\"RELEASE\\\"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.amethyst.${PRODUCT_NAME:rfc1034identifier}\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"\";\n\t\t\t\tWRAPPER_EXTENSION = app;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t40D95B4F1C6E2ED800AAF433 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tINFOPLIST_FILE = AmethystTests/Info.plist;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.amethyst.AmethystTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"AmethystTests/AmethystTests-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Amethyst.app/Contents/MacOS/Amethyst\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t40D95B501C6E2ED800AAF433 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEVELOPMENT_TEAM = \"\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tINFOPLIST_FILE = AmethystTests/Info.plist;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 11;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tOTHER_SWIFT_FLAGS = \"$(inherited)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.amethyst.AmethystTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"AmethystTests/AmethystTests-Bridging-Header.h\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Amethyst.app/Contents/MacOS/Amethyst\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t402DB6D91742E41A00D1C936 /* Build configuration list for PBXProject \"Amethyst\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t402DB6F91742E41A00D1C936 /* Debug */,\n\t\t\t\t402DB6FA1742E41A00D1C936 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t402DB6FB1742E41A00D1C936 /* Build configuration list for PBXNativeTarget \"Amethyst\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t402DB6FC1742E41A00D1C936 /* Debug */,\n\t\t\t\t402DB6FD1742E41A00D1C936 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t40D95B4E1C6E2ED800AAF433 /* Build configuration list for PBXNativeTarget \"AmethystTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t40D95B4F1C6E2ED800AAF433 /* Debug */,\n\t\t\t\t40D95B501C6E2ED800AAF433 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\t400D48F22A92AF1E0082750F /* XCRemoteSwiftPackageReference \"Cartography\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/robb/Cartography\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 4.0.0;\n\t\t\t};\n\t\t};\n\t\t400D48F52A92AF9B0082750F /* XCRemoteSwiftPackageReference \"LoginServiceKit\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/Clipy/LoginServiceKit\";\n\t\t\trequirement = {\n\t\t\t\tbranch = master;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n\t\t400D48F82A92B0130082750F /* XCRemoteSwiftPackageReference \"Sparkle\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sparkle-project/Sparkle\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 2.0.0;\n\t\t\t};\n\t\t};\n\t\t400D48FB2A92B06A0082750F /* XCRemoteSwiftPackageReference \"SwiftyJSON\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/SwiftyJSON/SwiftyJSON\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 5.0.0;\n\t\t\t};\n\t\t};\n\t\t400D48FE2A92B0A00082750F /* XCRemoteSwiftPackageReference \"Yams\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/jpsim/Yams\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 5.0.0;\n\t\t\t};\n\t\t};\n\t\t400F2DF12AABF4FC00C1AAE2 /* XCRemoteSwiftPackageReference \"KeyboardShortcuts\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sindresorhus/KeyboardShortcuts\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 2.0.0;\n\t\t\t};\n\t\t};\n\t\t402DA3D82F53DCFE00B08CA2 /* XCRemoteSwiftPackageReference \"silica\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/ianyh/silica\";\n\t\t\trequirement = {\n\t\t\t\tbranch = master;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n\t\t40AE15E12A92E9AF00E14536 /* XCRemoteSwiftPackageReference \"SwiftyBeaver\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/SwiftyBeaver/SwiftyBeaver\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.9.5;\n\t\t\t};\n\t\t};\n\t\t40AE15E42A92EA7400E14536 /* XCRemoteSwiftPackageReference \"RxSwift\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/ReactiveX/RxSwift\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 6.0.0;\n\t\t\t};\n\t\t};\n\t\t40AE15EC2A92EBD800E14536 /* XCRemoteSwiftPackageReference \"Quick\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/Quick/Quick\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 6.1.0;\n\t\t\t};\n\t\t};\n\t\t40AE15EF2A92EC5300E14536 /* XCRemoteSwiftPackageReference \"Nimble\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/Quick/Nimble\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 11.2.1;\n\t\t\t};\n\t\t};\n\t\t40CF37BE29B440A100CDB07A /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/apple/swift-argument-parser.git\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.0.0;\n\t\t\t};\n\t\t};\n\t\t40D540EE2ABFC7560007F40A /* XCRemoteSwiftPackageReference \"MASShortcut\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/shpakovski/MASShortcut\";\n\t\t\trequirement = {\n\t\t\t\tbranch = master;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\t400D48F32A92AF1E0082750F /* Cartography */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400D48F22A92AF1E0082750F /* XCRemoteSwiftPackageReference \"Cartography\" */;\n\t\t\tproductName = Cartography;\n\t\t};\n\t\t400D48F62A92AF9B0082750F /* LoginServiceKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400D48F52A92AF9B0082750F /* XCRemoteSwiftPackageReference \"LoginServiceKit\" */;\n\t\t\tproductName = LoginServiceKit;\n\t\t};\n\t\t400D48F92A92B0130082750F /* Sparkle */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400D48F82A92B0130082750F /* XCRemoteSwiftPackageReference \"Sparkle\" */;\n\t\t\tproductName = Sparkle;\n\t\t};\n\t\t400D48FC2A92B06B0082750F /* SwiftyJSON */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400D48FB2A92B06A0082750F /* XCRemoteSwiftPackageReference \"SwiftyJSON\" */;\n\t\t\tproductName = SwiftyJSON;\n\t\t};\n\t\t400D48FF2A92B0A00082750F /* Yams */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400D48FE2A92B0A00082750F /* XCRemoteSwiftPackageReference \"Yams\" */;\n\t\t\tproductName = Yams;\n\t\t};\n\t\t400F2DF22AABF4FC00C1AAE2 /* KeyboardShortcuts */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 400F2DF12AABF4FC00C1AAE2 /* XCRemoteSwiftPackageReference \"KeyboardShortcuts\" */;\n\t\t\tproductName = KeyboardShortcuts;\n\t\t};\n\t\t402DA3D62F53DCDB00B08CA2 /* Silica */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Silica;\n\t\t};\n\t\t402DA3D92F53DCFE00B08CA2 /* Silica */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 402DA3D82F53DCFE00B08CA2 /* XCRemoteSwiftPackageReference \"silica\" */;\n\t\t\tproductName = Silica;\n\t\t};\n\t\t40AE15E22A92E9AF00E14536 /* SwiftyBeaver */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40AE15E12A92E9AF00E14536 /* XCRemoteSwiftPackageReference \"SwiftyBeaver\" */;\n\t\t\tproductName = SwiftyBeaver;\n\t\t};\n\t\t40AE15E52A92EA7500E14536 /* RxCocoa */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40AE15E42A92EA7400E14536 /* XCRemoteSwiftPackageReference \"RxSwift\" */;\n\t\t\tproductName = RxCocoa;\n\t\t};\n\t\t40AE15E72A92EA7500E14536 /* RxSwift */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40AE15E42A92EA7400E14536 /* XCRemoteSwiftPackageReference \"RxSwift\" */;\n\t\t\tproductName = RxSwift;\n\t\t};\n\t\t40AE15ED2A92EBD800E14536 /* Quick */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40AE15EC2A92EBD800E14536 /* XCRemoteSwiftPackageReference \"Quick\" */;\n\t\t\tproductName = Quick;\n\t\t};\n\t\t40AE15F02A92EC5300E14536 /* Nimble */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40AE15EF2A92EC5300E14536 /* XCRemoteSwiftPackageReference \"Nimble\" */;\n\t\t\tproductName = Nimble;\n\t\t};\n\t\t40CF37BF29B440A100CDB07A /* ArgumentParser */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40CF37BE29B440A100CDB07A /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */;\n\t\t\tproductName = ArgumentParser;\n\t\t};\n\t\t40D540EF2ABFC7560007F40A /* MASShortcut */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = 40D540EE2ABFC7560007F40A /* XCRemoteSwiftPackageReference \"MASShortcut\" */;\n\t\t\tproductName = MASShortcut;\n\t\t};\n\t\t40E2D22F2D544F1E00D16B87 /* Silica */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Silica;\n\t\t};\n\t\t40EFFB492F0226EF00EDD929 /* Silica */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Silica;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 402DB6D61742E41A00D1C936 /* Project object */;\n}\n"
  },
  {
    "path": "Amethyst.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:Amethyst.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Amethyst.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"originHash\" : \"0bdfbd20a0602e0ca7fe7531ef1f5ef5df49b4edc989a6121656a1209da2ca9e\",\n  \"pins\" : [\n    {\n      \"identity\" : \"cartography\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/robb/Cartography\",\n      \"state\" : {\n        \"revision\" : \"b75197ea134f42b5feafb04b526b37eb1a41034b\",\n        \"version\" : \"4.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"cwlcatchexception\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/mattgallagher/CwlCatchException.git\",\n      \"state\" : {\n        \"revision\" : \"3b123999de19bf04905bc1dfdb76f817b0f2cc00\",\n        \"version\" : \"2.1.2\"\n      }\n    },\n    {\n      \"identity\" : \"cwlpreconditiontesting\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/mattgallagher/CwlPreconditionTesting.git\",\n      \"state\" : {\n        \"revision\" : \"a23ded2c91df9156628a6996ab4f347526f17b6b\",\n        \"version\" : \"2.1.2\"\n      }\n    },\n    {\n      \"identity\" : \"keyboardshortcuts\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sindresorhus/KeyboardShortcuts\",\n      \"state\" : {\n        \"revision\" : \"1aef85578fdd4f9eaeeb8d53b7b4fc31bf08fe27\",\n        \"version\" : \"2.4.0\"\n      }\n    },\n    {\n      \"identity\" : \"loginservicekit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Clipy/LoginServiceKit\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"a8e68051aca8bbb702e62ab36006a301966ab053\"\n      }\n    },\n    {\n      \"identity\" : \"masshortcut\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/shpakovski/MASShortcut\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"6f2603c6b6cc18f64a799e5d2c9d3bbc467c413a\"\n      }\n    },\n    {\n      \"identity\" : \"nimble\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Quick/Nimble\",\n      \"state\" : {\n        \"revision\" : \"eb5e3d717224fa0d1f6aff3fc2c5e8e81fa1f728\",\n        \"version\" : \"11.2.2\"\n      }\n    },\n    {\n      \"identity\" : \"quick\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Quick/Quick\",\n      \"state\" : {\n        \"revision\" : \"16910e406be96e08923918315388c3e989deac9e\",\n        \"version\" : \"6.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"rxswift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ReactiveX/RxSwift\",\n      \"state\" : {\n        \"revision\" : \"9dcaa4b333db437b0fbfaf453fad29069044a8b4\",\n        \"version\" : \"6.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"sparkle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sparkle-project/Sparkle\",\n      \"state\" : {\n        \"revision\" : \"f0ceaf5cc9f3f23daa0ccb6dcebd79fc96ccc7d9\",\n        \"version\" : \"2.5.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"8f4d2753f0e4778c76d5f05ad16c74f707390531\",\n        \"version\" : \"1.2.3\"\n      }\n    },\n    {\n      \"identity\" : \"swiftybeaver\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/SwiftyBeaver/SwiftyBeaver\",\n      \"state\" : {\n        \"revision\" : \"12b5acf96d98f91d50de447369bd18df74600f1a\",\n        \"version\" : \"1.9.6\"\n      }\n    },\n    {\n      \"identity\" : \"swiftyjson\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/SwiftyJSON/SwiftyJSON\",\n      \"state\" : {\n        \"revision\" : \"b3dcd7dbd0d488e1a7077cb33b00f2083e382f07\",\n        \"version\" : \"5.0.1\"\n      }\n    },\n    {\n      \"identity\" : \"yams\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/jpsim/Yams\",\n      \"state\" : {\n        \"revision\" : \"0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3\",\n        \"version\" : \"5.0.6\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "Amethyst.xcodeproj/xcshareddata/xcschemes/Amethyst Debug CLI.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1500\"\n   version = \"2.0\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"112751D77191BEF32260D8AB1BBBB8F6\"\n               BuildableName = \"Pods_Amethyst.framework\"\n               BlueprintName = \"Pods-Amethyst\"\n               ReferencedContainer = \"container:Pods/Pods.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n               BuildableName = \"Amethyst.app\"\n               BlueprintName = \"Amethyst\"\n               ReferencedContainer = \"container:Amethyst.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"NO\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"27D37F8A877E0F46B8656E1A4E38A5F7\"\n               BuildableName = \"Pods_AmethystTests.framework\"\n               BlueprintName = \"Pods-AmethystTests\"\n               ReferencedContainer = \"container:Pods/Pods.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"NO\"\n      codeCoverageEnabled = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n            BuildableName = \"Amethyst.app\"\n            BlueprintName = \"Amethyst\"\n            ReferencedContainer = \"container:Amethyst.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"40D95B461C6E2ED800AAF433\"\n               BuildableName = \"AmethystTests.xctest\"\n               BlueprintName = \"AmethystTests\"\n               ReferencedContainer = \"container:Amethyst.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"NO\"\n      debugXPCServices = \"NO\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\"\n      viewDebuggingEnabled = \"No\"\n      queueDebuggingEnabled = \"No\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n            BuildableName = \"Amethyst.app\"\n            BlueprintName = \"Amethyst\"\n            ReferencedContainer = \"container:Amethyst.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"help app\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"debug windows\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"windows\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n            BuildableName = \"Amethyst.app\"\n            BlueprintName = \"Amethyst\"\n            ReferencedContainer = \"container:Amethyst.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Amethyst.xcodeproj/xcshareddata/xcschemes/Amethyst.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1500\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"112751D77191BEF32260D8AB1BBBB8F6\"\n               BuildableName = \"Pods_Amethyst.framework\"\n               BlueprintName = \"Pods-Amethyst\"\n               ReferencedContainer = \"container:Pods/Pods.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n               BuildableName = \"Amethyst.app\"\n               BlueprintName = \"Amethyst\"\n               ReferencedContainer = \"container:Amethyst.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"NO\"\n            buildForProfiling = \"NO\"\n            buildForArchiving = \"NO\"\n            buildForAnalyzing = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"27D37F8A877E0F46B8656E1A4E38A5F7\"\n               BuildableName = \"Pods_AmethystTests.framework\"\n               BlueprintName = \"Pods-AmethystTests\"\n               ReferencedContainer = \"container:Pods/Pods.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      codeCoverageEnabled = \"YES\">\n      <TestPlans>\n         <TestPlanReference\n            reference = \"container:Amethyst.xctestplan\"\n            default = \"YES\">\n         </TestPlanReference>\n      </TestPlans>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"40D95B461C6E2ED800AAF433\"\n               BuildableName = \"AmethystTests.xctest\"\n               BlueprintName = \"AmethystTests\"\n               ReferencedContainer = \"container:Amethyst.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"NO\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n            BuildableName = \"Amethyst.app\"\n            BlueprintName = \"Amethyst\"\n            ReferencedContainer = \"container:Amethyst.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"402DB6DD1742E41A00D1C936\"\n            BuildableName = \"Amethyst.app\"\n            BlueprintName = \"Amethyst\"\n            ReferencedContainer = \"container:Amethyst.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Amethyst.xctestplan",
    "content": "{\n  \"configurations\" : [\n    {\n      \"id\" : \"3A3BA42F-5862-48E3-9505-9D3676A1BA59\",\n      \"name\" : \"Standard\",\n      \"options\" : {\n        \"commandLineArgumentEntries\" : [\n          {\n            \"argument\" : \"test\"\n          }\n        ]\n      }\n    }\n  ],\n  \"defaultOptions\" : {\n    \"targetForVariableExpansion\" : {\n      \"containerPath\" : \"container:Amethyst.xcodeproj\",\n      \"identifier\" : \"402DB6DD1742E41A00D1C936\",\n      \"name\" : \"Amethyst\"\n    }\n  },\n  \"testTargets\" : [\n    {\n      \"target\" : {\n        \"containerPath\" : \"container:Amethyst.xcodeproj\",\n        \"identifier\" : \"40D95B461C6E2ED800AAF433\",\n        \"name\" : \"AmethystTests\"\n      }\n    }\n  ],\n  \"version\" : 1\n}\n"
  },
  {
    "path": "Amethyst.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Amethyst.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Amethyst.xctestplan\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Amethyst.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Amethyst.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"originHash\" : \"5c090c150afce6ddb786afef9aa71f0237da58318f433cf9eea579c374d84d51\",\n  \"pins\" : [\n    {\n      \"identity\" : \"cartography\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/robb/Cartography\",\n      \"state\" : {\n        \"revision\" : \"b75197ea134f42b5feafb04b526b37eb1a41034b\",\n        \"version\" : \"4.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"cwlcatchexception\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/mattgallagher/CwlCatchException.git\",\n      \"state\" : {\n        \"revision\" : \"3b123999de19bf04905bc1dfdb76f817b0f2cc00\",\n        \"version\" : \"2.1.2\"\n      }\n    },\n    {\n      \"identity\" : \"cwlpreconditiontesting\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/mattgallagher/CwlPreconditionTesting.git\",\n      \"state\" : {\n        \"revision\" : \"a23ded2c91df9156628a6996ab4f347526f17b6b\",\n        \"version\" : \"2.1.2\"\n      }\n    },\n    {\n      \"identity\" : \"keyboardshortcuts\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sindresorhus/KeyboardShortcuts\",\n      \"state\" : {\n        \"revision\" : \"7ecc38bb6edf7d087d30e737057b8d8a9b7f51eb\",\n        \"version\" : \"2.2.4\"\n      }\n    },\n    {\n      \"identity\" : \"loginservicekit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Clipy/LoginServiceKit\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"a8e68051aca8bbb702e62ab36006a301966ab053\"\n      }\n    },\n    {\n      \"identity\" : \"masshortcut\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/shpakovski/MASShortcut\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"6f2603c6b6cc18f64a799e5d2c9d3bbc467c413a\"\n      }\n    },\n    {\n      \"identity\" : \"nimble\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Quick/Nimble\",\n      \"state\" : {\n        \"revision\" : \"eb5e3d717224fa0d1f6aff3fc2c5e8e81fa1f728\",\n        \"version\" : \"11.2.2\"\n      }\n    },\n    {\n      \"identity\" : \"quick\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/Quick/Quick\",\n      \"state\" : {\n        \"revision\" : \"16910e406be96e08923918315388c3e989deac9e\",\n        \"version\" : \"6.1.0\"\n      }\n    },\n    {\n      \"identity\" : \"rxswift\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ReactiveX/RxSwift\",\n      \"state\" : {\n        \"revision\" : \"9dcaa4b333db437b0fbfaf453fad29069044a8b4\",\n        \"version\" : \"6.6.0\"\n      }\n    },\n    {\n      \"identity\" : \"silica\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/ianyh/silica\",\n      \"state\" : {\n        \"branch\" : \"master\",\n        \"revision\" : \"237b2aeba4e59279e2d4e2b0fe637994f9823257\"\n      }\n    },\n    {\n      \"identity\" : \"sparkle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sparkle-project/Sparkle\",\n      \"state\" : {\n        \"revision\" : \"0ca3004e98712ea2b39dd881d28448630cce1c99\",\n        \"version\" : \"2.7.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser.git\",\n      \"state\" : {\n        \"revision\" : \"41982a3656a71c768319979febd796c6fd111d5c\",\n        \"version\" : \"1.5.0\"\n      }\n    },\n    {\n      \"identity\" : \"swiftybeaver\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/SwiftyBeaver/SwiftyBeaver\",\n      \"state\" : {\n        \"revision\" : \"12b5acf96d98f91d50de447369bd18df74600f1a\",\n        \"version\" : \"1.9.6\"\n      }\n    },\n    {\n      \"identity\" : \"swiftyjson\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/SwiftyJSON/SwiftyJSON\",\n      \"state\" : {\n        \"revision\" : \"af76cf3ef710b6ca5f8c05f3a31307d44a3c5828\",\n        \"version\" : \"5.0.2\"\n      }\n    },\n    {\n      \"identity\" : \"yams\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/jpsim/Yams\",\n      \"state\" : {\n        \"revision\" : \"3036ba9d69cf1fd04d433527bc339dc0dc75433d\",\n        \"version\" : \"5.1.3\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "AmethystTests/AmethystTests-Bridging-Header.h",
    "content": "\n"
  },
  {
    "path": "AmethystTests/Helpers/FrameAssignmentVerification.swift",
    "content": "//\n//  FrameAssignmentVerification.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Foundation\nimport Nimble\n\nextension RandomAccessCollection where Element == FrameAssignmentOperation<TestWindow>, Index == Int {\n    func filtered(byIDs ids: [TestWindow.WindowID]) -> [Element] {\n        return filter { ids.contains($0.frameAssignment.window.id) }\n    }\n\n    func forWindows<C: RandomAccessCollection>(_ windows: C) -> [Element] where C.Element == TestWindow, C.Index == Index {\n        return filtered(byIDs: Array(windows).map { $0.id() })\n    }\n\n    func sorted() -> [Element] {\n        return sorted { $0.frameAssignment.frame.origin.x < $1.frameAssignment.frame.origin.x }\n            .sorted { $0.frameAssignment.frame.origin.y < $1.frameAssignment.frame.origin.y }\n    }\n\n    func frames() -> [CGRect] {\n        return map { $0.frameAssignment.frame }\n    }\n\n    func description(withExpectedFrames frames: [CGRect]) -> String {\n        return zip(self.map { $0.frameAssignment }, frames).map { assignment, frame in\n            return \"\\(assignment.window.id):\\n\\tFrame: \\(assignment.frame)\\n\\tExpected: \\(frame)\"\n        }.joined(separator: \"\\n\")\n    }\n\n    func verify(frames: [CGRect], inOrder: Bool = false) {\n        expect(self.count).to(equal(frames.count), description: \"\\(count) assignments, but \\(frames.count) frames\")\n\n        if inOrder {\n            zip(self.map { $0.frameAssignment }, frames).forEach { assignment, frame in\n                expect(assignment.frame).to(equal(frame))\n            }\n        } else {\n            let currentFrames = map { $0.frameAssignment.frame }\n            for frame in frames {\n                expect(currentFrames).to(contain(frame))\n            }\n        }\n    }\n\n    func verify(frames: [String: CGRect]) {\n        var unverifiedFrames = frames\n        for operation in self {\n            let id = operation.frameAssignment.window.id\n            expect(unverifiedFrames[id]).toNot(beNil(), description: \"\\(id) should exist\")\n            expect(operation.frameAssignment.frame).to(equal(unverifiedFrames[id]), description: \"\\(id)\")\n            unverifiedFrames[id] = nil\n        }\n        expect(unverifiedFrames).to(beEmpty())\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Helpers/TestBundle.swift",
    "content": "//\n//  TestBundle.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 7/8/21.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\nimport Foundation\n\nextension Bundle {\n    static var testBundle: Bundle {\n        return Bundle(for: TestWindow.self)\n    }\n\n    static func layoutFile(key: String) -> URL? {\n        return testBundle.path(forResource: key, ofType: \"js\").flatMap { URL(fileURLWithPath: $0) }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>BNDL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/extended.js",
    "content": "function layout() {\n    return {\n        name: \"Extended Tall\",\n        extends: \"tall\",\n        getFrameAssignments: (windows, screenFrame, state, extendedFrames) => {\n            const maxX = Math.max(...extendedFrames.map(f => f.frame.x));\n            const minX = Math.min(...extendedFrames.map(f => f.frame.x));\n            return extendedFrames.reduce((frames, extendedFrame) => {\n                const frame = {\n                    x: extendedFrame.frame.x === minX ? maxX : minX,\n                    y: extendedFrame.frame.y,\n                    width: extendedFrame.frame.width,\n                    height: extendedFrame.frame.height\n                };\n                return { ...frames, [extendedFrame.id]: frame };\n            }, {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/fullscreen.js",
    "content": "function layout() {\n    return {\n        name: \"Fullscreen\",\n        getFrameAssignments: (windows, screenFrame) => {\n            return windows.reduce((frames, w) => ({ ...frames, [w.id]: screenFrame }), {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/null.js",
    "content": "function layout() {\n    return {\n        name: \"Null\",\n        getFrameAssignments: null\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/recommended-main-pane-ratio.js",
    "content": "function layout() {\n    return {\n        name: \"Ratio\",\n        initialState: {\n            mainPaneRatio: 0.5\n        },\n        recommendMainPaneRatio: (ratio, state) => {\n            return { ...state, mainPaneRatio: ratio };\n        },\n        getFrameAssignments: (windows, screenFrame, state) => {\n            return windows.reduce((frames, window, index) => {\n                if (index === 0) {\n                    const frame = {\n                        x: screenFrame.x,\n                        y: screenFrame.y,\n                        width: screenFrame.width * state.mainPaneRatio,\n                        height: screenFrame.height,\n                        isMain: true,\n                        unconstrainedDimension: \"horizontal\"\n                    };\n                    return { ...frames, [window.id]: frame };\n                } else {\n                    const frame = {\n                        x: screenFrame.x + screenFrame.width * state.mainPaneRatio,\n                        y: screenFrame.y,\n                        width: screenFrame.width - screenFrame.width * state.mainPaneRatio,\n                        height: screenFrame.height,\n                        isMain: false,\n                        unconstrainedDimension: \"horizontal\"\n                    };\n                    return { ...frames, [window.id]: frame };\n                }\n            }, {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/static-ratio-tall-native-commands.js",
    "content": "function layout() {\n    return {\n        name: \"Static Ratio Tall with Native Commands\",\n        initialState: {\n            mainPaneCount: 1\n        },\n        commands: {\n            increaseMain: {\n                description: \"Increase main pane count\",\n                updateState: (state) => {\n                    return { ...state, mainPaneCount: state.mainPaneCount + 1 };\n                }\n            },\n            decreaseMain: {\n                description: \"Decrease main pane count\",\n                updateState: (state) => {\n                    return { ...state, mainPaneCount: Math.max(1, state.mainPaneCount - 1) };\n                }\n            }\n        },\n        getFrameAssignments: (windows, screenFrame, state) => {\n            const mainPaneCount = Math.min(state.mainPaneCount, windows.length);\n            const secondaryPaneCount = windows.length - mainPaneCount;\n            const hasSecondaryPane = secondaryPaneCount > 0;\n\n            const mainPaneWindowHeight = Math.round(screenFrame.height / mainPaneCount);\n            const secondaryPaneWindowHeight = Math.round(hasSecondaryPane ? (screenFrame.height / secondaryPaneCount) : 0);\n\n            return windows.reduce((frames, window, index) => {\n                const isMain = index < mainPaneCount;\n                let frame;\n                if (isMain) {\n                    frame = {\n                        x: screenFrame.x,\n                        y: screenFrame.y + mainPaneWindowHeight * index,\n                        width: screenFrame.width / 2,\n                        height: mainPaneWindowHeight\n                    };\n                } else {\n                    frame = {\n                        x: screenFrame.x + screenFrame.width / 2,\n                        y: screenFrame.y + secondaryPaneWindowHeight * (index - mainPaneCount),\n                        width: screenFrame.width / 2,\n                        height: secondaryPaneWindowHeight\n                    }\n                }\n                return { ...frames, [window.id]: frame };\n            }, {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/static-ratio-tall.js",
    "content": "function layout() {\n    return {\n        name: \"Static Ratio Tall\",\n        initialState: {\n            mainPaneCount: 1\n        },\n        commands: {\n            command3: {\n                description: \"Increase main pane count\",\n                updateState: (state) => {\n                    return { ...state, mainPaneCount: state.mainPaneCount + 1 };\n                }\n            },\n            command4: {\n                description: \"Decrease main pane count\",\n                updateState: (state) => {\n                    return { ...state, mainPaneCount: Math.max(1, state.mainPaneCount - 1) };\n                }\n            }\n        },\n        getFrameAssignments: (windows, screenFrame, state) => {\n            const mainPaneCount = Math.min(state.mainPaneCount, windows.length);\n            const secondaryPaneCount = windows.length - mainPaneCount;\n            const hasSecondaryPane = secondaryPaneCount > 0;\n\n            const mainPaneWindowHeight = Math.round(screenFrame.height / mainPaneCount);\n            const secondaryPaneWindowHeight = Math.round(hasSecondaryPane ? (screenFrame.height / secondaryPaneCount) : 0);\n\n            return windows.reduce((frames, window, index) => {\n                const isMain = index < mainPaneCount;\n                let frame;\n                if (isMain) {\n                    frame = {\n                        x: screenFrame.x,\n                        y: screenFrame.y + mainPaneWindowHeight * index,\n                        width: screenFrame.width / 2,\n                        height: mainPaneWindowHeight\n                    };\n                } else {\n                    frame = {\n                        x: screenFrame.x + screenFrame.width / 2,\n                        y: screenFrame.y + secondaryPaneWindowHeight * (index - mainPaneCount),\n                        width: screenFrame.width / 2,\n                        height: secondaryPaneWindowHeight\n                    }\n                }\n                return { ...frames, [window.id]: frame };\n            }, {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/subset.js",
    "content": "function layout() {\n    return {\n        name: \"Subset\",\n        initialState: {\n            ids: []\n        },\n        commands: {\n            command3: {\n                description: \"Add window to subset\",\n                updateState: (state, focusedWindowID) => {\n                    const ids = state.ids;\n                    if (!!focusedWindowID) {\n                        const index = ids.indexOf(focusedWindowID);\n                        if (index === -1) {\n                            ids.push(focusedWindowID);\n                        }\n                    }\n                    return { ...state, ids };\n                }\n            },\n            command4: {\n                description: \"Remove window from subset\",\n                updateState: (state, focusedWindowID) => {\n                    const ids = state.ids;\n                    if (!!focusedWindowID) {\n                        const index = ids.indexOf(focusedWindowID);\n                        if (index > -1) {\n                            ids.splice(index, 1);\n                        }\n                    }\n                    return { ...state, ids };\n                }\n            }\n        },\n        getFrameAssignments: (windows, screenFrame, state) => {\n            const mainPaneCount = state.ids.length;\n            const secondaryPaneCount = Math.max(windows.length - mainPaneCount, 0);\n            const hasSecondaryPane = secondaryPaneCount > 0;\n\n            const mainPaneWindowWidth = hasSecondaryPane ? screenFrame.width / 2 : screenFrame.width;\n            const mainPaneWindowHeight = Math.round(screenFrame.height / mainPaneCount);\n            const secondaryPaneWindowHeight = Math.round(hasSecondaryPane ? (screenFrame.height / secondaryPaneCount) : 0);\n            let mainIndex = 0;\n            let secondaryIndex = 0;\n\n            return windows.reduce((frames, window) => {\n                const isMain = state.ids.includes(window.id);\n                let frame;\n                if (isMain) {\n                    frame = {\n                        x: screenFrame.x,\n                        y: screenFrame.y + mainPaneWindowHeight * mainIndex,\n                        width: mainPaneWindowWidth,\n                        height: mainPaneWindowHeight\n                    };\n                    mainIndex++;\n                } else {\n                    frame = {\n                        x: screenFrame.x + screenFrame.width / 2,\n                        y: screenFrame.y + secondaryPaneWindowHeight * secondaryIndex,\n                        width: screenFrame.width / 2,\n                        height: secondaryPaneWindowHeight\n                    };\n                    secondaryIndex++;\n                }\n                return { ...frames, [window.id]: frame };\n            }, {});\n        },\n        updateWithChange: (change, state) => {\n            switch (change.change) {\n                case \"window_swap\":\n                    if (state.ids.includes(change.windowID) && !state.ids.includes(change.otherWindowID)) {\n                        const index = state.ids.indexOf(change.windowID);\n                        state.ids.splice(index, 1);\n                        state.ids.push(change.otherWindowID);\n                    } else if (state.ids.includes(change.otherWindowID) && !state.ids.includes(change.windowID)) {\n                        const index = state.ids.indexOf(change.otherWindowID);\n                        state.ids.splice(index, 1);\n                        state.ids.push(change.windowID);\n                    }\n                    break;\n            }\n\n            return state;\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/undefined.js",
    "content": "function layout() {\n    return {\n        name: \"Undefined\"\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/uniform-columns.js",
    "content": "function layout() {\n    return {\n        name: \"Uniform Columns\",\n        getFrameAssignments: (windows, screenFrame) => {\n            const columnWidth = screenFrame.width / windows.length;\n            const frames = windows.map((window, index) => {\n                const frame = {\n                    x: screenFrame.x + (columnWidth * index),\n                    y: screenFrame.y,\n                    width: columnWidth,\n                    height: screenFrame.height\n                };\n                return { [window.id]: frame };\n            });\n            return frames.reduce((frames, frame) => ({ ...frames, ...frame }), {});\n        }\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/TestScreen.swift",
    "content": "//\n//  TestScreen.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Foundation\nimport SwiftyJSON\n\nfinal class TestScreen: ScreenType {\n    static var availableScreens: [TestScreen] = []\n    static var screensHaveSeparateSpaces = true\n\n    static func screenDescriptions() -> [JSON]? {\n        return []\n    }\n\n    private let id: String = UUID().uuidString\n    private let internalFrame: CGRect\n\n    init(frame: CGRect) {\n        internalFrame = frame\n    }\n\n    convenience init() {\n        let frame = CGRect(x: 0, y: 0, width: round(CGFloat.random(in: 500...2000)), height: round(CGFloat.random(in: 500...2000)))\n        self.init(frame: frame)\n    }\n\n    func adjustedFrame(disableWindowMargins: Bool) -> CGRect {\n        return internalFrame\n    }\n\n    func frameIncludingDockAndMenu() -> CGRect {\n        return internalFrame\n    }\n\n    func frameWithoutDockOrMenu() -> CGRect {\n        return internalFrame\n    }\n\n    func frame() -> CGRect {\n        return internalFrame\n    }\n\n    func screenID() -> String? {\n        return id\n    }\n\n    func focusScreen() {\n\n    }\n\n    static func == (lhs: TestScreen, rhs: TestScreen) -> Bool {\n        return lhs.id == rhs.id\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Model/TestWindow.swift",
    "content": "//\n//  TestWindow.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Foundation\nimport Silica\n\nfinal class TestWindow: WindowType {\n    typealias Screen = TestScreen\n    typealias WindowID = String\n\n    static var focused: TestWindow?\n\n    private let element: SIAccessibilityElement?\n    private let cgWindowID = CGWindowID(Int.random(in: 1...1000))\n    private let uuid = UUID().uuidString\n    private var _frame: CGRect = .zero\n    var isFocusedValue = false\n\n    static func currentlyFocused() -> Self? {\n        return (focused as? Self)\n    }\n\n    required init?(element: SIAccessibilityElement?) {\n        self.element = element\n    }\n\n    func id() -> WindowID {\n        return uuid\n    }\n\n    func cgID() -> CGWindowID {\n        return cgWindowID\n    }\n\n    func frame() -> CGRect {\n        return _frame\n    }\n\n    func screen() -> Screen? {\n        return nil\n    }\n\n    func setFrame(_ frame: CGRect, withThreshold threshold: CGSize) {\n        _frame = frame\n    }\n\n    func isFocused() -> Bool {\n        return isFocusedValue\n    }\n\n    func pid() -> pid_t {\n        return pid_t(1234)\n    }\n\n    func title() -> String? {\n        return nil\n    }\n\n    func shouldBeManaged() -> Bool {\n        return true\n    }\n\n    func shouldFloat() -> Bool {\n        return false\n    }\n\n    func isActive() -> Bool {\n        return true\n    }\n\n    func focus() -> Bool {\n        return false\n    }\n\n    func minimize() -> Bool {\n      return false\n    }\n\n    func moveScaled(to screen: Screen) {\n\n    }\n\n    func isOnScreen() -> Bool {\n        return true\n    }\n\n    func move(toSpace space: UInt) {\n\n    }\n\n    func move(toSpaceAtIndex space: UInt) {\n\n    }\n\n    func move(toSpace spaceID: CGSSpaceID) {\n\n    }\n\n    static func == (lhs: TestWindow, rhs: TestWindow) -> Bool {\n        return lhs.id() == rhs.id()\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Categories/SIWindow+AmethystTests.swift",
    "content": "@testable import Amethyst\nimport Cocoa\nimport Nimble\nimport Quick\n\nclass SIWindowAmethystTests: QuickSpec {\n    override func spec() {\n        describe(\"approximate rect comparison\") {\n            it(\"tolerates some provided error\") {\n                let rect = CGRect(x: 100, y: 100, width: 100, height: 100)\n                let tolerance = CGRect(x: 10, y: 10, width: 10, height: 10)\n                let translations = [\n                    CGAffineTransform(translationX: 5, y: 5),\n                    CGAffineTransform(translationX: 5, y: 0),\n                    CGAffineTransform(translationX: 5, y: -5),\n                    CGAffineTransform(translationX: 0, y: 5),\n                    CGAffineTransform(translationX: 0, y: -5),\n                    CGAffineTransform(translationX: -5, y: 5),\n                    CGAffineTransform(translationX: -5, y: 0),\n                    CGAffineTransform(translationX: -5, y: -5),\n\n                    CGAffineTransform(scaleX: 1.05, y: 1.05),\n                    CGAffineTransform(scaleX: 1.05, y: 0.95),\n                    CGAffineTransform(scaleX: 0.95, y: 1.05),\n                    CGAffineTransform(scaleX: 0.95, y: 0.95)\n                ]\n\n                translations.forEach {\n                    expect(rect.applying($0).approximatelyEqual(to: rect, within: tolerance)).to(beTrue(), description: \"\\($0)\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Configuration/UserConfigurationTests.swift",
    "content": "//\n//  UserConfigurationTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Cocoa\nimport Nimble\nimport Quick\nimport SwiftyJSON\nimport Yams\n\nclass TestConfigurationStorage: ConfigurationStorage {\n    var storage: [ConfigurationKey: Any] = [:]\n\n    func object(forKey key: ConfigurationKey) -> Any? {\n        return storage[key]\n    }\n\n    func array(forKey key: ConfigurationKey) -> [Any]? {\n        return storage[key] as? [Any]\n    }\n\n    func bool(forKey key: ConfigurationKey) -> Bool {\n        return (storage[key] as? Bool) ?? false\n    }\n\n    func float(forKey key: ConfigurationKey) -> Float {\n        return (storage[key] as? Float) ?? 0\n    }\n\n    func stringArray(forKey key: ConfigurationKey) -> [String]? {\n        return storage[key] as? [String]\n    }\n\n    func set(_ value: Any?, forKey key: ConfigurationKey) {\n        storage[key] = value\n    }\n\n    func set(_ value: Bool, forKey key: ConfigurationKey) {\n        storage[key] = value\n    }\n}\n\nclass UserConfigurationTests: QuickSpec {\n    private class TestHotKeyRegistrar: HotKeyRegistrar {\n        private(set) var keyString: String?\n        private(set) var modifiers: AMModifierFlags?\n        private(set) var handler: (() -> Void)?\n        private(set) var defaultsKey: String?\n        private(set) var override: Bool?\n\n        init() {}\n\n        func registerHotKey(with string: String?, modifiers: AMModifierFlags?, handler: @escaping () -> Void, defaultsKey: String, override: Bool) {\n            keyString = string\n            self.modifiers = modifiers\n            self.handler = handler\n            self.defaultsKey = defaultsKey\n            self.override = override\n        }\n    }\n\n    private class TestBundleIdentifiable: BundleIdentifiable {\n        var bundleIdentifier: String?\n    }\n\n    override func spec() {\n        describe(\"constructing commands\") {\n            context(\"overrides\") {\n                it(\"when user configuration exists\") {\n                    var configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    let localConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"1\"\n                        ]\n                    ]\n                    configuration.configurationYAML = nil\n                    configuration.configurationJSON = JSON(localConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    var registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n\n                    configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.configurationYAML = localConfiguration\n                    configuration.configurationJSON = nil\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n                }\n\n                it(\"when custom mod1 is specified\") {\n                    var configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    let localConfiguration: [String: Any] = [\n                        \"mod1\": [\n                            \"command\"\n                        ]\n                    ]\n                    let defaultConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"1\"\n                        ]\n                    ]\n                    configuration.configurationYAML = nil\n                    configuration.configurationJSON = JSON(localConfiguration)\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    var registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n\n                    configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.configurationYAML = localConfiguration\n                    configuration.configurationJSON = nil\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n\n                }\n\n                it(\"when custom mod2 is specified\") {\n                    var configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    let localConfiguration: [String: Any] = [\n                        \"mod2\": [\n                            \"command\"\n                        ]\n                    ]\n                    let defaultConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"1\"\n                        ]\n                    ]\n                    configuration.configurationJSON = JSON(localConfiguration)\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    var registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n\n                    configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.configurationYAML = localConfiguration\n                    configuration.configurationJSON = nil\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.override).to(beTrue())\n                }\n            }\n\n            context(\"takes command\") {\n                it(\"from local configuration over default\") {\n                    var configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    let localConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"1\"\n                        ]\n                    ]\n                    let defaultConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"2\"\n                        ]\n                    ]\n                    configuration.configurationJSON = JSON(localConfiguration)\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    var registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.keyString).to(equal(\"1\"))\n\n                    configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.configurationJSON = JSON(localConfiguration)\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(registrar.keyString).to(equal(\"1\"))\n                }\n\n                it(\"from default in absence of local configuration\") {\n                    let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    let defaultConfiguration: [String: Any] = [\n                        \"test\": [\n                            \"mod\": \"mod1\",\n                            \"key\": \"1\"\n                        ]\n                    ]\n                    configuration.configurationJSON = JSON([:])\n                    configuration.defaultConfiguration = JSON(defaultConfiguration)\n                    configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                    let registrar = TestHotKeyRegistrar()\n\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n\n                    expect(configuration.defaultConfiguration?[\"test\"][\"key\"].string).to(equal(\"1\"))\n                    expect(registrar.keyString).to(equal(\"1\"))\n                }\n            }\n\n            it(\"does not crash for malformed commands\") {\n                var configuration = UserConfiguration(storage: TestConfigurationStorage())\n                let localConfiguration: [String: Any] = [\n                    \"test\": [\n                        \"key\": \"2\"\n                    ]\n                ]\n                let defaultConfiguration: [String: Any] = [\n                    \"test\": [\n                        \"mod\": \"mod1\",\n                        \"key\": \"2\"\n                    ]\n                ]\n                configuration.configurationYAML = nil\n                configuration.configurationJSON = JSON(localConfiguration)\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                var registrar = TestHotKeyRegistrar()\n\n                expect {\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n                }.toNot(throwError())\n\n                configuration = UserConfiguration(storage: TestConfigurationStorage())\n                configuration.configurationYAML = localConfiguration\n                configuration.configurationJSON = nil\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.modifier1 = configuration.modifierFlagsForStrings([\"command\"])\n\n                registrar = TestHotKeyRegistrar()\n\n                expect {\n                    configuration.constructCommand(for: registrar, commandKey: \"test\", handler: {})\n                }.toNot(throwError())\n            }\n        }\n\n        describe(\"floating application\") {\n            it(\"is not floating by default\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([] as Any?, forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.notFloating)))\n            }\n\n            it(\"floats for exact matches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"test.test.Test\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.floating)))\n            }\n\n            it(\"floats for wildcard matches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"test.test.*\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.floating)))\n            }\n\n            it(\"floats for prefixed wildcard matches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"*.Test\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.floating)))\n            }\n\n            it(\"floats for inline wildcard matches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"test.*.Test\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.foo.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.floating)))\n            }\n\n            it(\"does not float for exact mismatches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"test.test.Other\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.notFloating)))\n            }\n\n            it(\"does not float for wildcard mismatches\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                storage.set([\"test.other.*\"], forKey: .floatingBundleIdentifiers)\n\n                let bundleIdentifiable = TestBundleIdentifiable()\n                let title = UUID().uuidString\n                bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.notFloating)))\n            }\n\n            context(\"as whitelist\") {\n                it(\"does not float for a matching window title\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let floatingBundle = FloatingBundle(id: \"test.test.Test\", windowTitles: [\"test\"])\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    configuration.setFloatingBundles([floatingBundle])\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    let title = \"test\"\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.notFloating)))\n                }\n\n                it(\"does not float for a matching application with no specified window titles\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    storage.set([\"test.test.Test\"], forKey: .floatingBundleIdentifiers)\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    let title = UUID().uuidString\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)).to(equal(.reliable(.notFloating)))\n                }\n\n                it(\"floats for no specified applications\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    storage.set([], forKey: .floatingBundleIdentifiers)\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    let title = UUID().uuidString\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    let float = configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: title)\n                    expect(float).to(equal(.reliable(.floating)))\n                }\n            }\n\n            context(\"specified window titles\") {\n                it(\"only float windows with titles\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let floatingBundle = FloatingBundle(id: \"test.test.Test\", windowTitles: [\"test1\"])\n\n                    storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    configuration.setFloatingBundles([floatingBundle])\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"test1\")).to(equal(.reliable(.floating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"test2\")).to(equal(.reliable(.notFloating)))\n                }\n\n                it(\"only allow windows with titles\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let floatingBundle = FloatingBundle(id: \"test.test.Test\", windowTitles: [\"test1\"])\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    configuration.setFloatingBundles([floatingBundle])\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"test1\")).to(equal(.reliable(.notFloating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"test2\")).to(equal(.reliable(.floating)))\n                }\n\n                it(\"treats empty and nil titles as unreliable\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let floatingBundle = FloatingBundle(id: \"test.test.Test\", windowTitles: [\"test1\"])\n\n                    storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    configuration.setFloatingBundles([floatingBundle])\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"\")).to(equal(.unreliable(.notFloating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: nil)).to(equal(.unreliable(.notFloating)))\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"\")).to(equal(.unreliable(.floating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: nil)).to(equal(.unreliable(.floating)))\n                }\n\n                it(\"treats empty and nil titles as reliable if no title would match anyway\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n\n                    storage.set(true, forKey: .floatingBundleIdentifiersIsBlacklist)\n                    configuration.setFloatingBundles([])\n\n                    let bundleIdentifiable = TestBundleIdentifiable()\n                    bundleIdentifiable.bundleIdentifier = \"test.test.Test\"\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"\")).to(equal(.reliable(.notFloating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: nil)).to(equal(.reliable(.notFloating)))\n\n                    storage.set(false, forKey: .floatingBundleIdentifiersIsBlacklist)\n\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: \"\")).to(equal(.reliable(.floating)))\n                    expect(configuration.runningApplication(bundleIdentifiable, byDefaultFloatsForTitle: nil)).to(equal(.reliable(.floating)))\n                }\n            }\n        }\n\n        describe(\"focus follows mouse\") {\n            it(\"toggles\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(true, forKey: .focusFollowsMouse)\n\n                expect(configuration.focusFollowsMouse()).to(beTrue())\n\n                configuration.toggleFocusFollowsMouse()\n\n                expect(configuration.focusFollowsMouse()).to(beFalse())\n            }\n        }\n\n        describe(\"small window size\") {\n            it(\"returns default value when not set\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                expect(configuration.smallWindowSize()).to(equal(500))\n            }\n\n            it(\"returns configured value when set\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(Float(300), forKey: .smallWindowSize)\n\n                expect(configuration.smallWindowSize()).to(equal(300))\n            }\n\n            it(\"returns default value when set to zero\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(Float(0), forKey: .smallWindowSize)\n\n                expect(configuration.smallWindowSize()).to(equal(500))\n            }\n\n            it(\"returns default value when set to negative\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n\n                storage.set(Float(-100), forKey: .smallWindowSize)\n\n                expect(configuration.smallWindowSize()).to(equal(500))\n            }\n        }\n\n        describe(\"load configuration\") {\n            it(\"default configuration does not override existing configuration\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n                let existingLayouts = [\"wide\"]\n                let defaultConfiguration = [\n                    \"layouts\": [\n                        \"tall\"\n                    ]\n                ]\n\n                storage.set(existingLayouts, forKey: .layouts)\n\n                expect(configuration.layoutKeys()).to(equal(existingLayouts))\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.loadConfiguration()\n                expect(configuration.layoutKeys()).to(equal(existingLayouts))\n            }\n\n            it(\"local json configuration does override existing configuration\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n                let existingLayouts = [\"wide\"]\n                let localConfiguration = [\n                    \"layouts\": [\n                        \"fullscreen\"\n                    ]\n                ]\n                let defaultConfiguration = [\n                    \"layouts\": [\n                        \"tall\"\n                    ]\n                ]\n\n                storage.set(existingLayouts, forKey: .layouts)\n\n                expect(configuration.layoutKeys()).to(equal(existingLayouts))\n                configuration.configurationYAML = nil\n                configuration.configurationJSON = JSON(localConfiguration)\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.loadConfiguration()\n                expect(configuration.layoutKeys()).to(equal(localConfiguration[\"layouts\"]))\n            }\n\n            it(\"local yaml configuration does override existing configuration\") {\n                let storage = TestConfigurationStorage()\n                let configuration = UserConfiguration(storage: storage)\n                let existingLayouts = [\"wide\"]\n                let localConfiguration = [\n                    \"layouts\": [\n                        \"fullscreen\"\n                    ]\n                ]\n                let defaultConfiguration = [\n                    \"layouts\": [\n                        \"tall\"\n                    ]\n                ]\n\n                storage.set(existingLayouts, forKey: .layouts)\n\n                expect(configuration.layoutKeys()).to(equal(existingLayouts))\n                configuration.configurationYAML = localConfiguration\n                configuration.configurationJSON = nil\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.loadConfiguration()\n                expect(configuration.layoutKeys()).to(equal(localConfiguration[\"layouts\"]))\n            }\n\n            it(\"prefers yaml over json for local configuration\") {\n                let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                let yamlConfiguration = [\n                    \"layouts\": [\n                        \"fullscreen\"\n                    ]\n                ]\n                let jsonConfiguration = [\n                    \"layouts\": [\n                        \"tall\"\n                    ]\n                ]\n                let defaultConfiguration = [\n                    \"layouts\": [\n                        \"wide\"\n                    ]\n                ]\n                configuration.configurationYAML = yamlConfiguration\n                configuration.configurationJSON = JSON(jsonConfiguration)\n                configuration.defaultConfiguration = JSON(defaultConfiguration)\n                configuration.loadConfiguration()\n                expect(configuration.layoutKeys()).to(equal(yamlConfiguration[\"layouts\"]))\n            }\n        }\n\n        describe(\"floating bundles\") {\n            describe(\"returned\") {\n                it(\"handles both strings and objects\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let bundlesData: [Any] = [\n                        \"test.test.1\",\n                        [\n                            \"id\": \"test.test.2\",\n                            \"window-titles\": [\n                                \"dialog\"\n                            ]\n                        ],\n                        \"test.test.3\"\n                    ]\n\n                    storage.set(bundlesData, forKey: .floatingBundleIdentifiers)\n\n                    let bundles = configuration.floatingBundles()\n                    let expectedBundles = [\n                        FloatingBundle(id: \"test.test.1\", windowTitles: []),\n                        FloatingBundle(id: \"test.test.2\", windowTitles: [\"dialog\"]),\n                        FloatingBundle(id: \"test.test.3\", windowTitles: [])\n                    ]\n                    expect(bundles.count).to(equal(3))\n                    expect(bundles).to(equal(expectedBundles))\n                }\n            }\n\n            describe(\"set\") {\n                it(\"assigns bundles\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let bundles = [\n                        FloatingBundle(id: \"test.test.1\", windowTitles: []),\n                        FloatingBundle(id: \"test.test.2\", windowTitles: [\"dialog\"]),\n                        FloatingBundle(id: \"test.test.3\", windowTitles: [])\n                    ]\n\n                    configuration.setFloatingBundles(bundles)\n\n                    let bundlesData = storage.array(forKey: .floatingBundleIdentifiers).flatMap { JSON($0) }\n                    let expectedBundlesData: JSON = JSON([\n                        [\n                            \"id\": \"test.test.1\",\n                            \"window-titles\": []\n                        ],\n                        [\n                            \"id\": \"test.test.2\",\n                            \"window-titles\": [\n                                \"dialog\"\n                            ]\n                        ],\n                        [\n                            \"id\": \"test.test.3\",\n                            \"window-titles\": []\n                        ]\n                    ])\n                    expect(bundlesData).to(equal(expectedBundlesData))\n                }\n            }\n        }\n\n        describe(\"floating bundles\") {\n            describe(\"returned\") {\n                it(\"handles both strings and objects\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let bundlesData: [Any] = [\n                        // Normal string\n                        \"test.test.1\",\n                        // JSON formatted\n                        [\n                            \"id\": \"test.test.2\",\n                            \"window-titles\": [\n                                \"dialog\"\n                            ]\n                        ],\n                        // Another string\n                        \"test.test.3\",\n                        // YAML formatted\n                        [\n                            \"test.test.4\": [\n                                \"window-titles\": [\n                                    \"dialog2\"\n                                ]\n                            ]\n                        ]\n                    ]\n\n                    storage.set(bundlesData, forKey: .floatingBundleIdentifiers)\n\n                    let bundles = configuration.floatingBundles()\n                    let expectedBundles = [\n                        FloatingBundle(id: \"test.test.1\", windowTitles: []),\n                        FloatingBundle(id: \"test.test.2\", windowTitles: [\"dialog\"]),\n                        FloatingBundle(id: \"test.test.3\", windowTitles: []),\n                        FloatingBundle(id: \"test.test.4\", windowTitles: [\"dialog2\"])\n                    ]\n                    expect(bundles.count).to(equal(4))\n                    expect(bundles).to(equal(expectedBundles))\n                }\n            }\n\n            describe(\"set\") {\n                it(\"assigns bundles\") {\n                    let storage = TestConfigurationStorage()\n                    let configuration = UserConfiguration(storage: storage)\n                    let bundles = [\n                        FloatingBundle(id: \"test.test.1\", windowTitles: []),\n                        FloatingBundle(id: \"test.test.2\", windowTitles: [\"dialog\"]),\n                        FloatingBundle(id: \"test.test.3\", windowTitles: [])\n                    ]\n\n                    configuration.setFloatingBundles(bundles)\n\n                    let bundlesData = storage.array(forKey: .floatingBundleIdentifiers).flatMap { JSON($0) }\n                    let expectedBundlesData: JSON = JSON([\n                        [\n                            \"id\": \"test.test.1\",\n                            \"window-titles\": []\n                        ],\n                        [\n                            \"id\": \"test.test.2\",\n                            \"window-titles\": [\n                                \"dialog\"\n                            ]\n                        ],\n                        [\n                            \"id\": \"test.test.3\",\n                            \"window-titles\": []\n                        ]\n                    ])\n                    expect(bundlesData).to(equal(expectedBundlesData))\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/BinarySpacePartitioningLayoutTests.swift",
    "content": "//\n//  BinarySpacePartitioningLayoutTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/29/16.\n//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Cocoa\nimport Nimble\nimport Quick\n\nclass BinarySpacePartitioningLayoutTests: QuickSpec {\n    override func spec() {\n        describe(\"TreeNode\") {\n            describe(\"finding\") {\n                it(\"finds a node that exists\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n                    let node3 = TreeNode<TestWindow>()\n                    let node4 = TreeNode<TestWindow>()\n\n                    node1.parent = rootNode\n                    node1.left = node2\n                    node1.right = node3\n\n                    node2.windowID = \"0\"\n                    node2.parent = node1\n\n                    node3.windowID = \"1\"\n                    node3.parent = node1\n\n                    node4.windowID = \"2\"\n                    node4.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node4\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.findWindowID(\"0\")).to(equal(node2))\n                    expect(rootNode.findWindowID(\"1\")).to(equal(node3))\n                    expect(rootNode.findWindowID(\"2\")).to(equal(node4))\n                }\n\n                it(\"does not find a node that does not exist\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n\n                    node1.windowID = \"0\"\n                    node1.parent = rootNode\n                    node2.windowID = \"1\"\n                    node2.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node2\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.findWindowID(\"2\")).to(beNil())\n                }\n            }\n\n            describe(\"traversing\") {\n                it(\"generates an empty list for an empty tree\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    expect(rootNode.orderedWindowIDs()).to(equal([]))\n                }\n\n                it(\"generates a correctly ordered list\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n                    let node3 = TreeNode<TestWindow>()\n                    let node4 = TreeNode<TestWindow>()\n\n                    node1.parent = rootNode\n                    node1.left = node2\n                    node1.right = node3\n\n                    node2.windowID = \"0\"\n                    node2.parent = node1\n\n                    node3.windowID = \"1\"\n                    node3.parent = node1\n\n                    node4.windowID = \"2\"\n                    node4.parent = rootNode\n\n                    rootNode.left = node4\n                    rootNode.right = node1\n\n                    let orderedList = rootNode.orderedWindowIDs()\n\n                    expect(orderedList).to(equal([\"2\", \"0\", \"1\"]))\n                }\n            }\n\n            describe(\"insertion\") {\n                it(\"inserts at end\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n\n                    node1.windowID = \"0\"\n                    node1.parent = rootNode\n                    node2.windowID = \"1\"\n                    node2.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node2\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n\n                    rootNode.insertWindowIDAtEnd(\"2\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.right?.left?.windowID).to(equal(\"1\"))\n                    expect(rootNode.right?.right?.windowID).to(equal(\"2\"))\n\n                    rootNode.insertWindowIDAtEnd(\"3\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.right?.left?.windowID).to(equal(\"1\"))\n                    expect(rootNode.right?.right?.left?.windowID).to(equal(\"2\"))\n                    expect(rootNode.right?.right?.right?.windowID).to(equal(\"3\"))\n                }\n\n                it(\"inserts at the insertion point\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n\n                    node1.windowID = \"0\"\n                    node1.parent = rootNode\n                    node2.windowID = \"1\"\n                    node2.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node2\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n\n                    rootNode.insertWindowID(\"2\", atPoint: \"0\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.left?.windowID).to(beNil())\n                    expect(rootNode.left?.left?.windowID).to(equal(\"0\"))\n                    expect(rootNode.left?.right?.windowID).to(equal(\"2\"))\n\n                    rootNode.insertWindowID(\"3\", atPoint: \"2\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.left?.windowID).to(beNil())\n                    expect(rootNode.left?.right?.windowID).to(beNil())\n                    expect(rootNode.left?.right?.left?.windowID).to(equal(\"2\"))\n                    expect(rootNode.left?.right?.right?.windowID).to(equal(\"3\"))\n\n                    rootNode.insertWindowID(\"4\", atPoint: \"0\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.left?.windowID).to(beNil())\n                    expect(rootNode.left?.left?.windowID).to(beNil())\n                    expect(rootNode.left?.left?.left?.windowID).to(equal(\"0\"))\n                    expect(rootNode.left?.left?.right?.windowID).to(equal(\"4\"))\n                }\n\n                it(\"inserts\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n\n                    node1.windowID = \"0\"\n                    node1.parent = rootNode\n                    node2.windowID = \"1\"\n                    node2.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node2\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n\n                    node1.insertWindowID(\"2\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.left?.windowID).to(beNil())\n                    expect(rootNode.left?.left?.windowID).to(equal(\"0\"))\n                    expect(rootNode.left?.right?.windowID).to(equal(\"2\"))\n                }\n\n                it(\"sets root value when the tree is empty\") {\n                    let rootNode = TreeNode<TestWindow>()\n\n                    rootNode.insertWindowIDAtEnd(\"0\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.windowID).to(equal(\"0\"))\n                }\n\n                it(\"clears root value when inserting value after first one\") {\n                    let rootNode = TreeNode<TestWindow>()\n\n                    rootNode.insertWindowIDAtEnd(\"0\")\n                    rootNode.insertWindowIDAtEnd(\"1\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.left).toNot(beNil())\n                    expect(rootNode.right).toNot(beNil())\n                    expect(rootNode.windowID).to(beNil())\n                }\n            }\n\n            describe(\"removing\") {\n                it(\"removes from a shallow tree\") {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n                    let node3 = TreeNode<TestWindow>()\n                    let node4 = TreeNode<TestWindow>()\n\n                    node1.parent = rootNode\n                    node1.left = node2\n                    node1.right = node3\n\n                    node2.windowID = \"0\"\n                    node2.parent = node1\n\n                    node3.windowID = \"1\"\n                    node3.parent = node1\n\n                    node4.windowID = \"2\"\n                    node4.parent = rootNode\n\n                    rootNode.left = node1\n                    rootNode.right = node4\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n\n                    rootNode.removeWindowID(\"1\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.findWindowID(\"1\")).to(beNil())\n                    expect(rootNode.left?.windowID).to(equal(\"0\"))\n\n                    rootNode.removeWindowID(\"2\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.findWindowID(\"2\")).to(beNil())\n                    expect(rootNode.windowID).to(equal(\"0\"))\n                }\n\n                it(\"removes from a deep tree\") {\n                    let rootNode = TreeNode<TestWindow>()\n\n                    (0..<10).forEach { rootNode.insertWindowIDAtEnd(\"\\($0)\") }\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n\n                    rootNode.removeWindowID(\"5\")\n\n                    expect(rootNode.treeIsValid()).to(beTrue())\n                    expect(rootNode.findWindowID(\"5\")).to(beNil())\n                }\n            }\n\n            describe(\"validitity\") {\n                it(\"is invalid when a node has children and an id at the same time\") {\n                    let node = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n                    let node3 = TreeNode<TestWindow>()\n\n                    node.windowID = \"0\"\n                    node2.windowID = \"1\"\n                    node3.windowID = \"2\"\n\n                    expect(node.valid).to(beTrue())\n\n                    node.left = node2\n                    node.right = node3\n\n                    expect(node.valid).to(beFalse())\n                }\n            }\n\n            it(\"deep compares\") {\n                let treeGenerator: () -> TreeNode<TestWindow> = {\n                    let rootNode = TreeNode<TestWindow>()\n                    let node1 = TreeNode<TestWindow>()\n                    let node2 = TreeNode<TestWindow>()\n                    let node3 = TreeNode<TestWindow>()\n                    let node4 = TreeNode<TestWindow>()\n\n                    node1.parent = rootNode\n                    node1.left = node2\n                    node1.right = node3\n\n                    node2.windowID = \"0\"\n                    node2.parent = node1\n\n                    node3.windowID = \"1\"\n                    node3.parent = node1\n\n                    node4.windowID = \"2\"\n                    node4.parent = rootNode\n\n                    rootNode.left = node4\n                    rootNode.right = node1\n\n                    return rootNode\n                }\n                let tree1 = treeGenerator()\n                let tree2 = treeGenerator()\n                let tree3 = treeGenerator()\n\n                tree3.right?.right?.windowID = \"5\"\n\n                expect(tree1 == tree2).to(beTrue())\n                expect(tree1 == tree3).to(beFalse())\n            }\n        }\n\n        describe(\"layout\") {\n            it(\"splits into binary partitions\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n\n                let layout = BinarySpacePartitioningLayout<TestWindow>()\n                windows.forEach { layout.updateWithChange(.add(window: $0)) }\n\n                let assignments = layout.frameAssignments(windowSet, on: screen)!\n                assignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 500, height: 500),\n                    CGRect(x: 1500, y: 500, width: 500, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screens\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n\n                let layout = BinarySpacePartitioningLayout<TestWindow>()\n                windows.forEach { layout.updateWithChange(.add(window: $0)) }\n\n                let assignments = layout.frameAssignments(windowSet, on: screen)!\n                assignments.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000),\n                    CGRect(x: 1100, y: 100, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 500, height: 500),\n                    CGRect(x: 1600, y: 600, width: 500, height: 500)\n                ])\n            }\n\n            describe(\"adding windows\") {\n                it(\"partitions the focused frame\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n\n                    var layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: $0.isFocused())\n                    }\n                    var windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows.dropLast(),\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n\n                    let layout = BinarySpacePartitioningLayout<TestWindow>()\n                    windows.dropLast().forEach { layout.updateWithChange(.add(window: $0)) }\n\n                    var assignments = layout.frameAssignments(windowSet, on: screen)!\n                    var expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                    ]\n\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n\n                    windows[1].isFocusedValue = true\n                    layoutWindows[1] = LayoutWindow<TestWindow>(id: windows[1].id(), frame: windows[1].frame(), isFocused: windows[1].isFocused())\n                    layout.updateWithChange(.focusChanged(window: windows[1]))\n                    layout.updateWithChange(.add(window: windows.last!))\n\n                    windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n\n                    assignments = layout.frameAssignments(windowSet, on: screen)!.sorted()\n                    expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 500, height: 500),\n                        CGRect(x: 1500, y: 0, width: 500, height: 500),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                    ]\n\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n                }\n\n                it(\"partitions the last frame if nothing is focused\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: $0.isFocused())\n                    }\n                    var windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows.dropLast(),\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n\n                    let layout = BinarySpacePartitioningLayout<TestWindow>()\n                    windows.dropLast().forEach { layout.updateWithChange(.add(window: $0)) }\n\n                    var assignments = layout.frameAssignments(windowSet, on: screen)!\n                    var expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                    ]\n\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n\n                    layout.updateWithChange(.add(window: windows.last!))\n\n                    windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n\n                    assignments = layout.frameAssignments(windowSet, on: screen)!.sorted()\n                    expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 500, height: 500),\n                        CGRect(x: 1500, y: 500, width: 500, height: 500)\n                    ]\n\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n                }\n            }\n\n            describe(\"removing windows\") {\n                it(\"expands the sibling\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                    }\n                    let windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n\n                    let layout = BinarySpacePartitioningLayout<TestWindow>()\n                    windows.forEach { layout.updateWithChange(.add(window: $0)) }\n\n                    var assignments = layout.frameAssignments(windowSet, on: screen)!\n                    var expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 500, height: 500),\n                        CGRect(x: 1500, y: 500, width: 500, height: 500)\n                    ]\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n\n                    layout.updateWithChange(.remove(window: windows[1]))\n\n                    assignments = layout.frameAssignments(windowSet, on: screen)!\n                    expectedFrames = [\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                    ]\n                    expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n                }\n            }\n\n            it(\"constructs an initial tree if necessary\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n\n                let layout = BinarySpacePartitioningLayout<TestWindow>()\n\n                let assignments = layout.frameAssignments(windowSet, on: screen)!\n                let expectedFrames = [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 500, height: 500),\n                    CGRect(x: 1500, y: 500, width: 500, height: 500)\n                ]\n                expect(assignments.frames()).to(equal(expectedFrames), description: assignments.description(withExpectedFrames: expectedFrames))\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = BinarySpacePartitioningLayout<TestWindow>()\n                let window = TestWindow(element: nil)!\n\n                layout.updateWithChange(.add(window: window))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(BinarySpacePartitioningLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout).to(equal(layout))\n            }\n\n            it(\"maintains the tree\") {\n                let layout = BinarySpacePartitioningLayout<TestWindow>()\n                let windows = [TestWindow(element: nil)!, TestWindow(element: nil)!, TestWindow(element: nil)!]\n\n                layout.updateWithChange(.add(window: windows[0]))\n                layout.updateWithChange(.add(window: windows[1]))\n                layout.updateWithChange(.focusChanged(window: windows[0]))\n                layout.updateWithChange(.add(window: windows[2]))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(BinarySpacePartitioningLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout).to(equal(layout))\n\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n\n                let assignments = decodedLayout\n                    .frameAssignments(windowSet, on: screen)!\n                    .map { $0.frameAssignment }\n                    .map { [$0.window.id: $0.frame] }\n                let expectedAssignments = layout\n                    .frameAssignments(windowSet, on: screen)!\n                    .map { $0.frameAssignment }\n                    .map { [$0.window.id: $0.frame] }\n                expect(assignments).to(equal(expectedAssignments))\n            }\n        }\n    }\n}\n\nextension TreeNode {\n    fileprivate func treeIsValid() -> Bool {\n        var valid = self.valid\n        if let left = left, let right = right {\n            valid = valid && left.parent == self && right.parent == self\n            valid = valid && left.treeIsValid() && right.treeIsValid()\n        }\n        return valid\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/ColumnLayoutTests.swift",
    "content": "//\n//  ColumnLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/18/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass ColumnLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"separates windows into columns in main pane and columns in secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ColumnLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                // The main pane is full height and the first half of the screen\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))])\n\n                let secondaryFrames = secondaryAssignments.enumerated().map { index, _ in\n                    return CGRect(x: 1000.0 + 333.0 * CGFloat(index), y: 0, width: 333, height: 1000)\n                }\n\n                secondaryAssignments.verify(frames: secondaryFrames)\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ColumnLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                // The main pane is full height and the first half of the screen\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [CGRect(x: 100, y: 100, width: 1000, height: 1000)])\n\n                let secondaryFrames = secondaryAssignments.enumerated().map { index, _ in\n                    return CGRect(x: 1100.0 + 333.0 * CGFloat(index), y: 100, width: 333, height: 1000)\n                }\n\n                secondaryAssignments.verify(frames: secondaryFrames)\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ColumnLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 333, height: 1000),\n                    CGRect(x: 1333, y: 0, width: 333, height: 1000),\n                    CGRect(x: 1666, y: 0, width: 333, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 333, height: 1000),\n                    CGRect(x: 333, y: 0, width: 333, height: 1000),\n                    CGRect(x: 666, y: 0, width: 333, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ColumnLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 250, height: 1000),\n                    CGRect(x: 1750, y: 0, width: 250, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 750, height: 1000),\n                    CGRect(x: 1250, y: 0, width: 750, height: 1000)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = ColumnLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(ColumnLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/CustomLayoutTests.swift",
    "content": "//\n//  CustomLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 7/8/21.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Cocoa\nimport Nimble\nimport Quick\n\nclass CustomLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"undefined layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"undefined\", fileURL: Bundle.layoutFile(key: \"undefined\")!)\n                expect(layout.layoutName).to(equal(\"Undefined\"))\n            }\n\n            it(\"defines no frame assignments\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"undefined\", fileURL: Bundle.layoutFile(key: \"undefined\")!)\n\n                expect(layout.frameAssignments(windowSet, on: screen)).to(beNil())\n            }\n        }\n\n        describe(\"null layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"null\", fileURL: Bundle.layoutFile(key: \"null\")!)\n                expect(layout.layoutName).to(equal(\"Null\"))\n            }\n\n            it(\"defines no frame assignments\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"null\", fileURL: Bundle.layoutFile(key: \"null\")!)\n\n                expect(layout.frameAssignments(windowSet, on: screen)).to(beNil())\n            }\n        }\n\n        describe(\"fullscreen layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"fullscreen\", fileURL: Bundle.layoutFile(key: \"fullscreen\")!)\n                expect(layout.layoutName).to(equal(\"Fullscreen\"))\n            }\n\n            it(\"makes all windows fullscreen\") {\n                let screen = TestScreen()\n                TestScreen.availableScreens = [screen]\n\n                let windows = [TestWindow(element: nil)!, TestWindow(element: nil)!, TestWindow(element: nil)!]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"fullscreen\", fileURL: Bundle.layoutFile(key: \"fullscreen\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(frameAssignments.count).to(equal(layoutWindows.count))\n\n                frameAssignments.forEach { assignment in\n                    expect(assignment.frameAssignment.frame).to(equal(screen.adjustedFrame()))\n                    expect(assignment.frameAssignment.finalFrame).to(equal(screen.adjustedFrame()))\n                }\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [TestWindow(element: nil)!, TestWindow(element: nil)!, TestWindow(element: nil)!]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"fullscreen\", fileURL: Bundle.layoutFile(key: \"fullscreen\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(frameAssignments.count).to(equal(layoutWindows.count))\n\n                frameAssignments.forEach { assignment in\n                    expect(assignment.frameAssignment.frame).to(equal(screen.adjustedFrame()))\n                    expect(assignment.frameAssignment.finalFrame).to(equal(screen.adjustedFrame()))\n                }\n            }\n        }\n\n        describe(\"columns layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"uniform-columns\", fileURL: Bundle.layoutFile(key: \"uniform-columns\")!)\n                expect(layout.layoutName).to(equal(\"Uniform Columns\"))\n            }\n\n            it(\"puts windows in uniform columns\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"uniform-columns\", fileURL: Bundle.layoutFile(key: \"uniform-columns\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(frameAssignments.count).to(equal(layoutWindows.count))\n\n                let expectedFrames = [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ]\n\n                frameAssignments.verify(frames: expectedFrames)\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"uniform-columns\", fileURL: Bundle.layoutFile(key: \"uniform-columns\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(frameAssignments.count).to(equal(layoutWindows.count))\n\n                let expectedFrames = [\n                    CGRect(x: 100, y: 100, width: 500, height: 1000),\n                    CGRect(x: 600, y: 100, width: 500, height: 1000),\n                    CGRect(x: 1100, y: 100, width: 500, height: 1000),\n                    CGRect(x: 1600, y: 100, width: 500, height: 1000)\n                ]\n                frameAssignments.verify(frames: expectedFrames)\n            }\n        }\n\n        describe(\"static ratio tall layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall\")!)\n                expect(layout.layoutName).to(equal(\"Static Ratio Tall\"))\n            }\n\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall\")!)\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                layout.command3()\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.command3()\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n        }\n\n        describe(\"static ratio tall layout with proxied native commands\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall-native-commands\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall-native-commands\")!)\n                expect(layout.layoutName).to(equal(\"Static Ratio Tall with Native Commands\"))\n            }\n\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall-native-commands\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall-native-commands\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall-native-commands\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall-native-commands\")!)\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"static-ratio-tall-native-commands\", fileURL: Bundle.layoutFile(key: \"static-ratio-tall-native-commands\")!)\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                layout.increaseMainPaneCount()\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n        }\n\n        describe(\"subset layout\") {\n            it(\"defines a name\") {\n                let layout = CustomLayout<TestWindow>(key: \"subset\", fileURL: Bundle.layoutFile(key: \"subset\")!)\n                expect(layout.layoutName).to(equal(\"Subset\"))\n            }\n\n            describe(\"subset\") {\n                it(\"starts empty\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                    }\n                    let windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n                    let layout = CustomLayout<TestWindow>(key: \"subset\", fileURL: Bundle.layoutFile(key: \"subset\")!)\n                    let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                    frameAssignments.verify(frames: [\n                        CGRect(x: 1000, y: 0, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 250, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 750, width: 1000, height: 250)\n                    ], inOrder: false)\n                }\n\n                it(\"adds and removes ids to the subset\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                    }\n                    let windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n                    let layout = CustomLayout<TestWindow>(key: \"subset\", fileURL: Bundle.layoutFile(key: \"subset\")!)\n\n                    var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        CGRect(x: 1000, y: 0, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 250, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 750, width: 1000, height: 250)\n                    ])\n\n                    TestWindow.focused = windows[0]\n                    layout.command3()\n                    TestWindow.focused = windows[3]\n                    layout.command3()\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        CGRect(x: 0, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 500),\n                        CGRect(x: 0, y: 500, width: 1000, height: 500)\n                    ])\n\n                    TestWindow.focused = windows[0]\n                    layout.command4()\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                        CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                        CGRect(x: 1000, y: 666, width: 1000, height: 333),\n                        CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                    ])\n\n                    TestWindow.focused = windows[3]\n                    layout.command4()\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        CGRect(x: 1000, y: 0, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 250, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 500, width: 1000, height: 250),\n                        CGRect(x: 1000, y: 750, width: 1000, height: 250)\n                    ])\n                }\n\n                it(\"swaps subset state on window swaps\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                    }\n                    let windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n                    let layout = CustomLayout<TestWindow>(key: \"subset\", fileURL: Bundle.layoutFile(key: \"subset\")!)\n\n                    var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        windows[0].id(): CGRect(x: 1000, y: 0, width: 1000, height: 250),\n                        windows[1].id(): CGRect(x: 1000, y: 250, width: 1000, height: 250),\n                        windows[2].id(): CGRect(x: 1000, y: 500, width: 1000, height: 250),\n                        windows[3].id(): CGRect(x: 1000, y: 750, width: 1000, height: 250)\n                    ])\n\n                    TestWindow.focused = windows[0]\n                    layout.command3()\n                    TestWindow.focused = windows[3]\n                    layout.command3()\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        windows[0].id(): CGRect(x: 0, y: 0, width: 1000, height: 500),\n                        windows[1].id(): CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        windows[2].id(): CGRect(x: 1000, y: 500, width: 1000, height: 500),\n                        windows[3].id(): CGRect(x: 0, y: 500, width: 1000, height: 500)\n                    ])\n\n                    layout.updateWithChange(.windowSwap(window: windows[1], otherWindow: windows[0]))\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        windows[0].id(): CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        windows[1].id(): CGRect(x: 0, y: 0, width: 1000, height: 500),\n                        windows[2].id(): CGRect(x: 1000, y: 500, width: 1000, height: 500),\n                        windows[3].id(): CGRect(x: 0, y: 500, width: 1000, height: 500)\n                    ])\n\n                    layout.updateWithChange(.windowSwap(window: windows[3], otherWindow: windows[2]))\n\n                    frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                    frameAssignments.verify(frames: [\n                        windows[0].id(): CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                        windows[1].id(): CGRect(x: 0, y: 0, width: 1000, height: 500),\n                        windows[2].id(): CGRect(x: 0, y: 500, width: 1000, height: 500),\n                        windows[3].id(): CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                    ])\n                }\n            }\n        }\n\n        describe(\"extended\") {\n            describe(\"tall right\") {\n                it(\"swaps columns\") {\n                    let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                    TestScreen.availableScreens = [screen]\n\n                    let windows = [\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!,\n                        TestWindow(element: nil)!\n                    ]\n                    let layoutWindows = windows.map {\n                        LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                    }\n                    let windowSet = WindowSet<TestWindow>(\n                        windows: layoutWindows,\n                        isWindowWithIDActive: { _ in return true },\n                        isWindowWithIDFloating: { _ in return false },\n                        windowForID: { id in return windows.first { $0.id() == id } }\n                    )\n                    let layout = CustomLayout<TestWindow>(key: \"extended\", fileURL: Bundle.layoutFile(key: \"extended\")!)\n                    let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                    let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                    let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                    mainAssignment.verify(frames: [\n                        CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                    ])\n                    secondaryAssignments.verify(frames: [\n                        CGRect(x: 0, y: 0, width: 1000, height: 500),\n                        CGRect(x: 0, y: 500, width: 1000, height: 500)\n                    ])\n                }\n            }\n        }\n\n        describe(\"recommend main pane ratio\") {\n            it(\"receives correct ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let window = TestWindow(element: nil)!\n                let layoutWindow = LayoutWindow<TestWindow>(id: window.id(), frame: window.frame(), isFocused: false)\n                let windowSet = WindowSet<TestWindow>(\n                    windows: [layoutWindow],\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { _ in window }\n                )\n                let layout = CustomLayout<TestWindow>(key: \"recommended-main-pane-ratio\", fileURL: Bundle.layoutFile(key: \"recommended-main-pane-ratio\")!)\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignment = frameAssignments.forWindows([window])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                layout.recommendMainPaneRawRatio(rawRatio: 0.25)\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignment = frameAssignments.forWindows([window])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRawRatio(rawRatio: 0.75)\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignment = frameAssignments.forWindows([window])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/FloatingLayoutTests.swift",
    "content": "//\n//  FloatingLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass FloatingLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"generates no assignments\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = FloatingLayout<TestWindow>()\n\n                expect(layout.frameAssignments(windowSet, on: screen)).to(beNil())\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = FloatingLayout<TestWindow>()\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                expect {\n                    try JSONDecoder().decode(FloatingLayout<TestWindow>.self, from: encodedLayout)\n                }.toNot(throwError())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/FullscreenLayoutTests.swift",
    "content": "//\n//  FullscreenLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass FullscreenLayoutTests: QuickSpec {\n    private lazy var operationQueue: OperationQueue = {\n        let operationQueue = OperationQueue()\n        operationQueue.maxConcurrentOperationCount = 1\n        return operationQueue\n    }()\n\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"makes all windows fullscreen\") {\n                let screen = TestScreen()\n                TestScreen.availableScreens = [screen]\n\n                let windows = [TestWindow(element: nil)!, TestWindow(element: nil)!, TestWindow(element: nil)!]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = FullscreenLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                frameAssignments.forEach { assignment in\n                    expect(assignment.frameAssignment.frame).to(equal(screen.adjustedFrame()))\n                    expect(assignment.frameAssignment.finalFrame).to(equal(screen.adjustedFrame()))\n                }\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [TestWindow(element: nil)!, TestWindow(element: nil)!, TestWindow(element: nil)!]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = FullscreenLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                frameAssignments.forEach { assignment in\n                    expect(assignment.frameAssignment.frame).to(equal(screen.adjustedFrame()))\n                    expect(assignment.frameAssignment.finalFrame).to(equal(screen.adjustedFrame()))\n                }\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = FullscreenLayout<TestWindow>()\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                expect {\n                    try JSONDecoder().decode(FullscreenLayout<TestWindow>.self, from: encodedLayout)\n                }.toNot(throwError())\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/RowLayoutTests.swift",
    "content": "//\n//  RowLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass RowLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"separates windows into rows in main pane and rows in secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = RowLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                // The main pane is full width and the top half of the screen\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [CGRect(origin: .zero, size: CGSize(width: 2000, height: 500))])\n\n                let secondaryFrames = secondaryAssignments.enumerated().map { index, _ in\n                    return CGRect(x: 0, y: 500.0 + 166 * CGFloat(index), width: 2000, height: 166)\n                }\n\n                secondaryAssignments.verify(frames: secondaryFrames)\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = RowLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                // The main pane is full width and the top half of the screen\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [CGRect(x: 100, y: 100, width: 2000, height: 500)])\n\n                let secondaryFrames = secondaryAssignments.enumerated().map { index, _ in\n                    return CGRect(x: 100, y: 600.0 + 166 * CGFloat(index), width: 2000, height: 166)\n                }\n\n                secondaryAssignments.verify(frames: secondaryFrames)\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = RowLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 2000, height: 166),\n                    CGRect(x: 0, y: 666, width: 2000, height: 166),\n                    CGRect(x: 0, y: 832, width: 2000, height: 166)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 250),\n                    CGRect(x: 0, y: 250, width: 2000, height: 250)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 2000, height: 250),\n                    CGRect(x: 0, y: 750, width: 2000, height: 250)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 166),\n                    CGRect(x: 0, y: 166, width: 2000, height: 166),\n                    CGRect(x: 0, y: 332, width: 2000, height: 166)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 498, width: 2000, height: 500)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = RowLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 2000, height: 166),\n                    CGRect(x: 0, y: 666, width: 2000, height: 166),\n                    CGRect(x: 0, y: 832, width: 2000, height: 166)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 750)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 750, width: 2000, height: 83),\n                    CGRect(x: 0, y: 833, width: 2000, height: 83),\n                    CGRect(x: 0, y: 916, width: 2000, height: 83)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 250)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 250, width: 2000, height: 250),\n                    CGRect(x: 0, y: 500, width: 2000, height: 250),\n                    CGRect(x: 0, y: 750, width: 2000, height: 250)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = RowLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(RowLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/TallLayoutTests.swift",
    "content": "//\n//  TallLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass TallLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 500),\n                    CGRect(x: 1500, y: 500, width: 500, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 500),\n                    CGRect(x: 500, y: 500, width: 1500, height: 500)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = TallLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(TallLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/TallRightLayoutTests.swift",
    "content": "//\n//  TallRightLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/18/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass TallRightLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallRightLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 0, y: 0, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screens\") {\n                let screen = TestScreen(frame: CGRect(origin: CGPoint(x: 100, y: 100), size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallRightLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 100, y: 600, width: 1000, height: 500),\n                    CGRect(x: 100, y: 100, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallRightLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 666, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 0, width: 1000, height: 333)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 0, y: 0, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TallRightLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 0, y: 0, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 500, height: 500),\n                    CGRect(x: 0, y: 0, width: 500, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1500, height: 500),\n                    CGRect(x: 0, y: 0, width: 1500, height: 500)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = TallRightLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(TallRightLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/ThreeColumnLayoutTests.swift",
    "content": "//\n//  ThreeColumnLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/19/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass ThreeColumnLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"TriplePaneArrangement\") {\n            describe(\"pane counts\") {\n                it(\"takes windows in main pane up to provided count\") {\n                    let mainPaneCount: UInt = 2\n                    let screenSize = CGSize(width: 2000, height: 1000)\n                    let count: (UInt) -> UInt = { windowCount -> UInt in\n                        return TriplePaneArrangement(\n                            mainPane: .left,\n                            numWindows: windowCount,\n                            numMainPane: mainPaneCount,\n                            screenSize: screenSize,\n                            mainPaneRatio: 0.5\n                        ).count(.main)\n                    }\n\n                    expect(count(1)).to(equal(1))\n                    expect(count(2)).to(equal(2))\n                    expect(count(4)).to(equal(2))\n                }\n\n                it(\"splits non-main windows between two panes\") {\n                    let mainPaneCount: UInt = 0\n                    let screenSize = CGSize(width: 2000, height: 1000)\n                    let secondaryCount: (UInt) -> UInt = { windowCount -> UInt in\n                        return TriplePaneArrangement(\n                            mainPane: .left,\n                            numWindows: windowCount,\n                            numMainPane: mainPaneCount,\n                            screenSize: screenSize,\n                            mainPaneRatio: 0.5\n                        ).count(.secondary)\n                    }\n                    let tertiaryCount: (UInt) -> UInt = { windowCount -> UInt in\n                        return TriplePaneArrangement(\n                            mainPane: .left,\n                            numWindows: windowCount,\n                            numMainPane: mainPaneCount,\n                            screenSize: screenSize,\n                            mainPaneRatio: 0.5\n                        ).count(.tertiary)\n                    }\n\n                    expect(secondaryCount(1)).to(equal(1))\n                    expect(secondaryCount(2)).to(equal(1))\n                    expect(secondaryCount(3)).to(equal(2))\n                    expect(secondaryCount(4)).to(equal(2))\n\n                    expect(tertiaryCount(1)).to(equal(0))\n                    expect(tertiaryCount(2)).to(equal(1))\n                    expect(tertiaryCount(3)).to(equal(1))\n                    expect(tertiaryCount(4)).to(equal(2))\n                }\n            }\n\n            it(\"splits panes into rows\") {\n                let mainPaneCount: UInt = 2\n                let screenSize = CGSize(width: 2000, height: 1000)\n                let height: (UInt, Pane) -> CGFloat = { windowCount, pane -> CGFloat in\n                    return TriplePaneArrangement(\n                        mainPane: .left,\n                        numWindows: windowCount,\n                        numMainPane: mainPaneCount,\n                        screenSize: screenSize,\n                        mainPaneRatio: 0.5\n                    ).height(pane)\n                }\n\n                expect(height(1, .main)).to(equal(1000))\n                expect(height(2, .main)).to(equal(500))\n                expect(height(3, .main)).to(equal(500))\n                expect(height(1, .secondary)).to(equal(0))\n                expect(height(2, .secondary)).to(equal(0))\n                expect(height(3, .secondary)).to(equal(1000))\n                expect(height(4, .secondary)).to(equal(1000))\n                expect(height(5, .secondary)).to(equal(500))\n                expect(height(6, .secondary)).to(equal(500))\n                expect(height(1, .tertiary)).to(equal(0))\n                expect(height(2, .tertiary)).to(equal(0))\n                expect(height(3, .tertiary)).to(equal(0))\n                expect(height(4, .tertiary)).to(equal(1000))\n                expect(height(5, .tertiary)).to(equal(1000))\n                expect(height(6, .tertiary)).to(equal(500))\n            }\n        }\n\n        describe(\"middle layout\") {\n            it(\"separates into a main pane and two secondary panes\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnMiddleLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"handles non-origin screens\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnMiddleLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 600, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 500, height: 1000),\n                    CGRect(x: 1600, y: 100, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnMiddleLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 500),\n                    CGRect(x: 0, y: 500, width: 500, height: 500),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1000, height: 500),\n                    CGRect(x: 500, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1000, height: 333),\n                    CGRect(x: 500, y: 333, width: 1000, height: 333),\n                    CGRect(x: 500, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnMiddleLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 250, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 250, height: 1000),\n                    CGRect(x: 1750, y: 0, width: 250, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 750, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 750, height: 1000),\n                    CGRect(x: 1250, y: 0, width: 750, height: 1000)\n                ])\n            }\n\n            describe(\"coding\") {\n                it(\"encodes and decodes\") {\n                    let layout = ThreeColumnMiddleLayout<TestWindow>()\n                    layout.increaseMainPaneCount()\n                    layout.recommendMainPaneRatio(0.45)\n\n                    expect(layout.mainPaneCount).to(equal(2))\n                    expect(layout.mainPaneRatio).to(equal(0.45))\n\n                    let encodedLayout = try! JSONEncoder().encode(layout)\n                    let decodedLayout = try! JSONDecoder().decode(ThreeColumnMiddleLayout<TestWindow>.self, from: encodedLayout)\n\n                    expect(decodedLayout.mainPaneCount).to(equal(2))\n                    expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n                }\n            }\n        }\n\n        describe(\"left layout\") {\n            it(\"separates into a main pane and two secondary panes\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnLeftLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnLeftLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 500, height: 1000),\n                    CGRect(x: 1600, y: 100, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnLeftLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 500),\n                    CGRect(x: 1000, y: 500, width: 500, height: 500),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnLeftLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 250, height: 1000),\n                    CGRect(x: 1750, y: 0, width: 250, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 750, height: 1000),\n                    CGRect(x: 1250, y: 0, width: 750, height: 1000)\n                ])\n            }\n\n            describe(\"coding\") {\n                it(\"encodes and decodes\") {\n                    let layout = ThreeColumnLeftLayout<TestWindow>()\n                    layout.increaseMainPaneCount()\n                    layout.recommendMainPaneRatio(0.45)\n\n                    expect(layout.mainPaneCount).to(equal(2))\n                    expect(layout.mainPaneRatio).to(equal(0.45))\n\n                    let encodedLayout = try! JSONEncoder().encode(layout)\n                    let decodedLayout = try! JSONDecoder().decode(ThreeColumnLeftLayout<TestWindow>.self, from: encodedLayout)\n\n                    expect(decodedLayout.mainPaneCount).to(equal(2))\n                    expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n                }\n            }\n        }\n\n        describe(\"right layout\") {\n            it(\"separates into a main pane and two secondary panes\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnRightLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"separates into a main pane and two secondary panes\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnRightLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 500, height: 1000),\n                    CGRect(x: 600, y: 100, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnRightLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 500),\n                    CGRect(x: 0, y: 500, width: 500, height: 500),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = ThreeColumnRightLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 250, height: 1000),\n                    CGRect(x: 250, y: 0, width: 250, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 750, height: 1000),\n                    CGRect(x: 750, y: 0, width: 750, height: 1000)\n                ])\n            }\n\n            describe(\"coding\") {\n                it(\"encodes and decodes\") {\n                    let layout = ThreeColumnRightLayout<TestWindow>()\n                    layout.increaseMainPaneCount()\n                    layout.recommendMainPaneRatio(0.45)\n\n                    expect(layout.mainPaneCount).to(equal(2))\n                    expect(layout.mainPaneRatio).to(equal(0.45))\n\n                    let encodedLayout = try! JSONEncoder().encode(layout)\n                    let decodedLayout = try! JSONDecoder().decode(ThreeColumnRightLayout<TestWindow>.self, from: encodedLayout)\n\n                    expect(decodedLayout.mainPaneCount).to(equal(2))\n                    expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/TwoPaneLayoutTests.swift",
    "content": "//\n//  TwoPaneLayoutTests.swift\n//  AmethystTests\n//\n//  Created by @mwz on 14/06/21.\n//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass TwoPaneLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout horizontal\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 1000),\n                    CGRect(x: 1100, y: 100, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"does not increase and decrease windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                let mainAssignments = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(1))\n\n                layout.decreaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(1))\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 1500, height: 1000)\n                ])\n            }\n        }\n\n        describe(\"layout vertical\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 1000, height: 2000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000),\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 1000, height: 2000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 100, y: 1100, width: 1000, height: 1000),\n                    CGRect(x: 100, y: 1100, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"does not increase and decrease windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 1000, height: 2000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                let mainAssignments = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000),\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000),\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(1))\n\n                layout.decreaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(1))\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 1000, height: 2000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = TwoPaneLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000),\n                    CGRect(x: 0, y: 1000, width: 1000, height: 1000)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 1500, width: 1000, height: 500),\n                    CGRect(x: 0, y: 1500, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 1500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 1500)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = TwoPaneLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(1))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(TwoPaneLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(1))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/WideLayoutTests.swift",
    "content": "//\n//  WideLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/7/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass WideLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"layout\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WideLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 2000, height: 500))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WideLayout<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 2000, height: 500)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 100, y: 600, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WideLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 667, height: 500),\n                    CGRect(x: 667, y: 500, width: 667, height: 500),\n                    CGRect(x: 1334, y: 500, width: 667, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 667, height: 500),\n                    CGRect(x: 667, y: 0, width: 667, height: 500),\n                    CGRect(x: 1334, y: 0, width: 667, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 2000, height: 500)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WideLayout<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 500)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 500, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 750)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 750, width: 1000, height: 250),\n                    CGRect(x: 1000, y: 750, width: 1000, height: 250)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 2000, height: 250)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 250, width: 1000, height: 750),\n                    CGRect(x: 1000, y: 250, width: 1000, height: 750)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = WideLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(WideLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Layout/WidescreenTallLayoutTests.swift",
    "content": "//\n//  WidescreenTallLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/18/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass WidescreenTallLayoutTests: QuickSpec {\n    override func spec() {\n        afterEach {\n            TestScreen.availableScreens = []\n        }\n\n        describe(\"left layout\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutLeft<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(origin: .zero, size: CGSize(width: 1000, height: 1000))\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"handles non-origin screen\") {\n                let screen = TestScreen(frame: CGRect(x: 100, y: 100, width: 2000, height: 1000))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutLeft<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 100, y: 100, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1100, y: 100, width: 1000, height: 500),\n                    CGRect(x: 1100, y: 600, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutLeft<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 333, width: 1000, height: 333),\n                    CGRect(x: 1000, y: 666, width: 1000, height: 333)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000),\n                    CGRect(x: 500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 333, height: 1000),\n                    CGRect(x: 333, y: 0, width: 333, height: 1000),\n                    CGRect(x: 666, y: 0, width: 333, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutLeft<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 500),\n                    CGRect(x: 1000, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 500),\n                    CGRect(x: 1500, y: 500, width: 500, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 500),\n                    CGRect(x: 500, y: 500, width: 1500, height: 500)\n                ])\n            }\n        }\n\n        describe(\"right layout\") {\n            it(\"separates into a main pane and a secondary pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutRight<TestWindow>()\n                let frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                let mainAssignment = frameAssignments.forWindows(windows[..<1])\n                let secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignment.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n            }\n\n            it(\"increases and decreases windows in the main pane\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutRight<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 333),\n                    CGRect(x: 0, y: 333, width: 1000, height: 333),\n                    CGRect(x: 0, y: 666, width: 1000, height: 333)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(2))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<2])\n                secondaryAssignments = frameAssignments.forWindows(windows[2...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 500, height: 1000),\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.increaseMainPaneCount()\n                expect(layout.mainPaneCount).to(equal(3))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<3])\n                secondaryAssignments = frameAssignments.forWindows(windows[3...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 333, height: 1000),\n                    CGRect(x: 1333, y: 0, width: 333, height: 1000),\n                    CGRect(x: 1666, y: 0, width: 333, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 1000)\n                ])\n            }\n\n            it(\"changes distribution based on pane ratio\") {\n                let screen = TestScreen(frame: CGRect(origin: .zero, size: CGSize(width: 2000, height: 1000)))\n                TestScreen.availableScreens = [screen]\n\n                let windows = [\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!,\n                    TestWindow(element: nil)!\n                ]\n                let layoutWindows = windows.map {\n                    LayoutWindow<TestWindow>(id: $0.id(), frame: $0.frame(), isFocused: false)\n                }\n                let windowSet = WindowSet<TestWindow>(\n                    windows: layoutWindows,\n                    isWindowWithIDActive: { _ in return true },\n                    isWindowWithIDFloating: { _ in return false },\n                    windowForID: { id in return windows.first { $0.id() == id } }\n                )\n                let layout = WidescreenTallLayoutRight<TestWindow>()\n\n                expect(layout.mainPaneCount).to(equal(1))\n\n                var frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                var mainAssignments = frameAssignments.forWindows(windows[..<1])\n                var secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1000, y: 0, width: 1000, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1000, height: 500),\n                    CGRect(x: 0, y: 500, width: 1000, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.75)\n                expect(layout.mainPaneRatio).to(equal(0.75))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 500, y: 0, width: 1500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 500, height: 500),\n                    CGRect(x: 0, y: 500, width: 500, height: 500)\n                ])\n\n                layout.recommendMainPaneRatio(0.25)\n                expect(layout.mainPaneRatio).to(equal(0.25))\n\n                frameAssignments = layout.frameAssignments(windowSet, on: screen)!\n                mainAssignments = frameAssignments.forWindows(windows[..<1])\n                secondaryAssignments = frameAssignments.forWindows(windows[1...])\n\n                mainAssignments.verify(frames: [\n                    CGRect(x: 1500, y: 0, width: 500, height: 1000)\n                ])\n\n                secondaryAssignments.verify(frames: [\n                    CGRect(x: 0, y: 0, width: 1500, height: 500),\n                    CGRect(x: 0, y: 500, width: 1500, height: 500)\n                ])\n            }\n        }\n\n        describe(\"coding\") {\n            it(\"encodes and decodes\") {\n                let layout = WidescreenTallLayout<TestWindow>()\n                layout.increaseMainPaneCount()\n                layout.recommendMainPaneRatio(0.45)\n\n                expect(layout.mainPaneCount).to(equal(2))\n                expect(layout.mainPaneRatio).to(equal(0.45))\n\n                let encodedLayout = try! JSONEncoder().encode(layout)\n                let decodedLayout = try! JSONDecoder().decode(WidescreenTallLayout<TestWindow>.self, from: encodedLayout)\n\n                expect(decodedLayout.mainPaneCount).to(equal(2))\n                expect(decodedLayout.mainPaneRatio).to(equal(0.45))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Managers/HotKeyManagerTests.swift",
    "content": "//\n//  HotKeyManagerTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 4/18/17.\n//  Copyright © 2017 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nclass HotKeyManagerTests: QuickSpec {\n    override func spec() {\n        describe(\"hotKeyNameToDefaultsKey\") {\n            it(\"has the right number of screens\") {\n                let keyMapping = HotKeyManager<SIApplication>.hotKeyNameToDefaultsKey()\n                let screenCommands = keyMapping.filter { $0[1].hasPrefix(CommandKey.focusScreenPrefix.rawValue) }\n                expect(screenCommands.count).to(equal(7))\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "AmethystTests/Tests/Managers/ScreenManagerTests.swift",
    "content": "//\n//  ScreenManagerTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 2/11/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.\n//\n\n@testable import Amethyst\nimport Nimble\nimport Quick\nimport Silica\n\nprivate final class TestDelegate: ScreenManagerDelegate {\n    typealias Window = TestWindow\n\n    func applyWindowLimit(forScreenManager screenManager: ScreenManager<TestDelegate>, minimizingIn range: (Int) -> Range<Int>) {\n        fatalError()\n    }\n    func activeWindowSet(forScreenManager screenManager: ScreenManager<TestDelegate>) -> WindowSet<TestWindow> {\n        fatalError()\n    }\n    func onReflowInitiation() {\n        fatalError()\n    }\n    func onReflowCompletion() {\n        fatalError()\n    }\n}\n\nclass ScreenManagerTests: QuickSpec {\n    override func spec() {\n        describe(\"coding\") {\n            it(\"decodes layouts\") {\n                let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                configuration.setLayoutKeys(LayoutType<TestWindow>.standardLayoutClasses().map { $0.layoutKey })\n\n                let layouts = LayoutType<TestWindow>.standardLayoutClasses().map { $0.init() }\n                let encoder = JSONEncoder()\n                let encodedLayouts = layouts.map { [\"key\": $0.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode($0)] }\n                let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                expect(decodedLayouts.count).to(equal(layouts.count))\n                expect(decodedLayouts.count).to(equal(encodedLayouts.count))\n                expect(decodedLayouts.map { $0.layoutKey }).to(equal(layouts.map { $0.layoutKey }))\n            }\n\n            it(\"replaces incorrectly encoded layouts\") {\n                let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                configuration.setLayoutKeys([FullscreenLayout<TestWindow>.layoutKey, TallLayout<TestWindow>.layoutKey])\n\n                let encoder = JSONEncoder()\n                let encodedLayouts = [\n                    [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                    [\"key\": TallLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode([\"incorrect\": \"encoding\"])]\n                ]\n                let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                expect {\n                    try JSONDecoder().decode(TallLayout<TestWindow>.self, from: encodedLayouts[1][\"data\"]!)\n                }.to(throwError())\n\n                expect(decodedLayouts.count).to(equal(2))\n                expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n            }\n\n            it(\"replaces incorrectly keyed layouts\") {\n                let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                configuration.setLayoutKeys([FullscreenLayout<TestWindow>.layoutKey, TallLayout<TestWindow>.layoutKey])\n\n                let encoder = JSONEncoder()\n                let encodedLayouts = [\n                    [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                    [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(TallLayout<TestWindow>())]\n                ]\n                let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                expect(decodedLayouts.count).to(equal(2))\n                expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n            }\n\n            context(\"layout list changes\") {\n                it(\"maintains encoded layouts on insertions\") {\n                    let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.setLayoutKeys([\n                        FullscreenLayout<TestWindow>.layoutKey,\n                        WideLayout<TestWindow>.layoutKey,\n                        TallLayout<TestWindow>.layoutKey\n                    ])\n\n                    let encoder = JSONEncoder()\n                    let tallLayout = TallLayout<TestWindow>()\n                    tallLayout.increaseMainPaneCount()\n\n                    expect(tallLayout.mainPaneCount).to(equal(2))\n\n                    let encodedLayouts = [\n                        [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                        [\"key\": TallLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(tallLayout)]\n                    ]\n                    let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                    expect(decodedLayouts.count).to(equal(3))\n                    expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n                    expect((decodedLayouts.last as? TallLayout<TestWindow>)?.mainPaneCount).to(equal(2))\n                }\n\n                it(\"maintains encoded layouts on appends\") {\n                    let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.setLayoutKeys([\n                        FullscreenLayout<TestWindow>.layoutKey,\n                        TallLayout<TestWindow>.layoutKey,\n                        WideLayout<TestWindow>.layoutKey\n                    ])\n\n                    let encoder = JSONEncoder()\n                    let tallLayout = TallLayout<TestWindow>()\n                    tallLayout.increaseMainPaneCount()\n\n                    expect(tallLayout.mainPaneCount).to(equal(2))\n\n                    let encodedLayouts = [\n                        [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                        [\"key\": TallLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(tallLayout)]\n                    ]\n                    let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                    expect(decodedLayouts.count).to(equal(3))\n                    expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n                    expect((decodedLayouts[1] as? TallLayout<TestWindow>)?.mainPaneCount).to(equal(2))\n                }\n\n                it(\"maintains encoded layouts on prepends\") {\n                    let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.setLayoutKeys([\n                        WideLayout<TestWindow>.layoutKey,\n                        FullscreenLayout<TestWindow>.layoutKey,\n                        TallLayout<TestWindow>.layoutKey\n                    ])\n\n                    let encoder = JSONEncoder()\n                    let tallLayout = TallLayout<TestWindow>()\n                    tallLayout.increaseMainPaneCount()\n\n                    expect(tallLayout.mainPaneCount).to(equal(2))\n\n                    let encodedLayouts = [\n                        [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                        [\"key\": TallLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(tallLayout)]\n                    ]\n                    let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                    expect(decodedLayouts.count).to(equal(3))\n                    expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n                    expect((decodedLayouts.last as? TallLayout<TestWindow>)?.mainPaneCount).to(equal(2))\n                }\n\n                it(\"maintains existing layouts on deletes\") {\n                    let configuration = UserConfiguration(storage: TestConfigurationStorage())\n                    configuration.setLayoutKeys([\n                        WideLayout<TestWindow>.layoutKey,\n                        TallLayout<TestWindow>.layoutKey\n                    ])\n\n                    let encoder = JSONEncoder()\n                    let wideLayout = WideLayout<TestWindow>()\n                    let tallLayout = TallLayout<TestWindow>()\n                    wideLayout.increaseMainPaneCount()\n                    tallLayout.increaseMainPaneCount()\n\n                    expect(wideLayout.mainPaneCount).to(equal(2))\n                    expect(tallLayout.mainPaneCount).to(equal(2))\n\n                    let encodedLayouts = [\n                        [\"key\": WideLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(wideLayout)],\n                        [\"key\": FullscreenLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(FullscreenLayout<TestWindow>())],\n                        [\"key\": TallLayout<TestWindow>.layoutKey.data(using: .utf8)!, \"data\": try! encoder.encode(tallLayout)]\n                    ]\n                    let decodedLayouts = try! ScreenManager<TestDelegate>.decodedLayouts(from: encodedLayouts, userConfiguration: configuration)\n\n                    expect(decodedLayouts.count).to(equal(2))\n                    expect(decodedLayouts.map { $0.layoutKey }).to(equal(configuration.layoutKeys()))\n                    expect((decodedLayouts.first as? WideLayout<TestWindow>)?.mainPaneCount).to(equal(2))\n                    expect((decodedLayouts.last as? TallLayout<TestWindow>)?.mainPaneCount).to(equal(2))\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Brewfile",
    "content": "# run 'brew bundle' to install all listed packages\n\n# Build tools\nbrew \"fastlane\" # Easiest way to build and release mobile apps\nbrew \"xcbeautify\" # Little beautifier tool for xcodebuild\nbrew \"swiftlint\"\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ianynda@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "LICENSE.md",
    "content": "Copyright (c) 2015 Ian Ynda-Hummel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Amethyst\n\n[![Discussions](https://img.shields.io/github/discussions/ianyh/Amethyst)](https://github.com/ianyh/Amethyst/discussions)\n[![Open Source Helpers](https://www.codetriage.com/ianyh/amethyst/badges/users.svg)](https://www.codetriage.com/ianyh/amethyst)\n[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)\n\nTiling window manager for macOS along the lines of [xmonad](https://xmonad.org/).\n\n![Windows](https://ianyh.com/amethyst/images/windows.png)\n\nIf you want to learn more about tiling window managers and the features of Amethyst there are some great community resources on YouTube.\n\n[Boost your MacOS PRODUCTIVITY with Amethyst | Tiling Window Manager](https://www.youtube.com/watch?v=7Z9-Ry4yGNc)\n\n## Getting Amethyst\n\nAmethyst is available for direct download on the [releases page](https://github.com/ianyh/Amethyst/releases) or using [homebrew cask](https://github.com/Homebrew/homebrew-cask).\n\n```\nbrew install --cask amethyst\n```\n\nNote: that Amethyst now is only supported on macOS 10.15+.\n\n## Using Amethyst\n\nAmethyst must be given permissions to use the accessibility APIs in the Privacy & Security tab, Privacy -> Accessibilty.\n\n<p align=\"center\">\n  <img style=\"text-align: center; border-radius:4px;\" width=\"500px\" src=\"docs/images/accessibility-permission.png\" alt=\"Give Accessibility permission to Amethyst under Privicay and Security.\" />\n</p>\n\n**_Important note_**: You will probably want to disable `Automatically rearrange Spaces based on most recent use` (found under Mission Control in System Preferences). This setting is enabled by default, and will cause your Spaces to swap places based on use. This makes keyboard navigation between Spaces unpredictable.\n\n<p align=\"center\">\n  <img style=\"text-align: center\" width=\"500px\" src=\"docs/images/auto-rearranage.png\" />\n</p>\n\nor run in a terminal:\n```bash\ndefaults write com.apple.dock workspaces-auto-swoosh -bool NO\nkillall Dock\n```\n\n## Troubleshooting\n\nSee [Troubleshooting](docs/troubleshooting.md) for some common issues.\n\n## Configuration\n\n### Keyboard Shortcuts\n\nAmethyst uses two modifier combinations (`mod1` and `mod2`) and can optionally use another two (`mod3` and `mod4`).\n\n| Default Shortcut | Description |\n|---|---|\n| `mod1` | `option + shift` |\n| `mod2` | `ctrl + option + shift` |\n| `mod3` | not defined by default |\n| `mod4` | not defined by default |\n\nAnd defines the following commands, mostly a mapping to xmonad key combinations.\n\n| Default Shortcut | Description |\n|---|---|\n| `mod1 + space` | Cycle layout forward |\n| `mod2 + space` | Cycle layout backwards |\n| `mod1 + h` | Shrink the main pane |\n| `mod1 + l` | Expand the main pane |\n| `mod1 + ,` | Increase main pane count |\n| `mod1 + .` | Decrease main pane count |\n| `mod1 + j` | Move focus counter clockwise |\n| `mod1 + k` | Move focus clockwise |\n| `mod1 + p` | Move focus to counter clockwise screen |\n| `mod1 + n` | Move focus to clockwise screen |\n| `mod2 + h` | Swap focused window to counter clockwise screen |\n| `mod2 + l` | Swap focused window to clockwise screen |\n| `mod2 + j` | Swap focused window counter clockwise |\n| `mod2 + k` | Swap focused window clockwise |\n| `mod1 + enter` | Swap focused window with main window |\n| `mod1 + z` | Force windows to be reevaluated |\n| `mod2 + z` | Relaunch Amethyst |\n| `mod2 + left` | Throw focused window to space left |\n| `mod2 + right` | Throw focused window to space right |\n| `mod2 + 1` | Throw focused window to space 1 |\n| `mod2 + 2` | Throw focused window to space 2 |\n| `mod2 + 3` | Throw focused window to space 3 |\n| `mod2 + 4` | Throw focused window to space 4 |\n| `mod2 + 5` | Throw focused window to space 5 |\n| `mod2 + 6` | Throw focused window to space 6 |\n| `mod2 + 7` | Throw focused window to space 7 |\n| `mod2 + 8` | Throw focused window to space 8 |\n| `mod2 + 9` | Throw focused window to space 9 |\n| `mod2 + 0` | Throw focused window to space 10 |\n| `none`     | Throw focused window to space 11 |\n| `none`     | Throw focused window to space 12 |\n| `none`     | Throw focused window to space 13 |\n| `none`     | Throw focused window to space 14 |\n| `none`     | Throw focused window to space 15 |\n| `none`     | Throw focused window to space 16 |\n| `mod1 + w` | Focus Screen 1 |\n| `mod2 + w` | Throw focused window to screen 1 |\n| `mod1 + e` | Focus Screen 2 |\n| `mod2 + e` | Throw focused window to screen 2 |\n| `mod1 + r` | Focus Screen 3 |\n| `mod2 + r` | Throw focused window to screen 3 |\n| `mod1 + q` | Focus Screen 4 |\n| `mod2 + q` | Throw focused window to screen 4 |\n| `mod1 + g` | Focus Screen 5 |\n| `mod2 + g` | Throw focused window to screen 5 |\n| `mod1 + t` | Toggle float for focused window |\n| `mod1 + i` | Display current layout |\n| `mod2 + t` | Toggle global tiling |\n| `mod1 + a` | Select tall layout |\n| `none` | Select tall-right layout |\n| `mod1 + s` | Select wide layout |\n| `none` | Select middle-wide layout |\n| `mod1 + d` | Select fullscreen layout |\n| `mod1 + f` | Select column layout |\n| `none` | Select row layout |\n| `none` | Select floating layout |\n| `none` | Select widescreen-tall layout |\n| `none` | Select bsp layout |\n\n### Available Layouts\n\nAmethyst allows you to cycle among several different window layouts.\nLayouts can also be enabled/disabled to control whether they appear in the cycle sequence at all.\n\n#### Tall\n\nThe default layout. This gives you one \"main pane\" on the left, and one other pane on the right. By default, one window is placed in the main pane (extending the full height of the screen), and all remaining windows are placed in the other pane. If either pane has more than one window, that pane will be evenly split into rows, to show them all. You can use the keyboard shortcuts above to control which window(s), and how many, are in the main pane, as well as the horizontal size of the main pane vs. the other pane.\n\n#### Tall-Right\n\nExactly the same as *Tall*, but the main pane is on the right, with the other pane on the left.\n\n#### Wide\n\nThe rotated version of *Tall*, where the main pane is on the _top_ (extending the full width of the screen), and the other pane is on the bottom. If either pane has more than one window, that pane will split into columns instead of rows.\n\n#### Two Pane\n\nThis layout has two visible panes - the main and the secondary pane. The window\nin the main pane is pinned, just like in other layouts, and all the remaining\nwindows are placed in the other pane with only one window being visible at a\ntime, which can be swapped (using the keyboard shortcuts). This layout\nautomatically adapts to horizontal/vertical tiling depending on your screen\norientation. The main pane is on the left in the horizontal orientation and it's\non the top in the vertical orientation.\n\n#### Two Pane Right\n\nExactly the same as *Two Pane*, but the main pane is on the right, with the other pane on the left.\n\n#### 3Column-Left\n\nA three-column version of *Tall*, with one main pane on the left (extending the full height of the screen) and two other panes, one in the middle and one on the right. Like *Tall*, if any pane has more than one window, that pane will be split into rows. You can control how many windows are in the main pane as usual; other windows will be assigned as evenly as possible between the other two panes.\n\n#### 3Column-Middle\n\nExactly like *3Column-Left*, but the main pane is in the middle, with the other panes on either side. (In previous versions of Amethyst, this layout was known as *Middle-Wide*.)\n\n#### 3Column-Right\n\nExactly like *3Column-Left*, but the main pane is on the right, with the other panes in the middle and on the left.\n\n#### Widescreen-Tall\n\n This mode is like *Tall*, but if there are multiple windows in the main pane,\n the main pane splits into columns rather than rows.\n The other pane still splits windows into rows, like *Tall*.\n This layout gets its name because it probably makes the most sense on very\n wide screens, with a large main pane consisting of several columns, and all\n remaining windows stacked into the final column.\n Other layouts that work well on very wide screens include any that allow for\n more than two columns (to take advantage of the screen width), such as\n any of the *3Column-&ast;* layouts, or *Column*.\n\n#### Fullscreen\n\nIn this layout, the currently focused window takes up the entire screen, and the other windows are not visible at all. You can rotate between each of the windows using the \"focus the next window\" shortcut, as usual.\n\n#### Column\n\nThis layout has one column per window, with each window extending the full height of the screen. The farthest-left window is considered the \"main\" window in the sense that you can change its size with the \"shrink/expand the main pane\" shortcuts; the other windows split the remaining space evenly.\n\n#### Row\n\nThe rotated version of *Column*, where each window takes up an entire row, extending the full width of the screen.\n\n#### Floating\n\nThis mode makes all windows \"floating\", allowing you to move and resize them as if Amethyst were temporarily deactivated. Unlike the other modes, this will mean that windows can be placed \"on top of\" each other, obscuring your view of some windows.\n\n#### Binary Space Partitioning (BSP)\n\nThis layout does not have a main pane in the way that other layouts do. When adding windows, any given pane can be split evenly into two panes along whatever axis is longer. This is recursive such that pane A can be split in the middle into pane A on the left and pane B on the right; pane B can then be split into pane B on top and pane C on bottom; pane C can then be split into pane C on the left and pane D on the right; and so on.\n\n#### Custom (beta)\n\nCustom layouts can be implemented via JavaScript. See [Custom Layouts](docs/custom-layouts.md).\n\n### Configuration File\n\nAmethyst supports configuration via YAML in the home directory. See [Configuration Files](docs/configuration-files.md). Note that if configuration file is present, it will override the settings defined via the GUI.\n\n## Building Amethyst Locally\n\nIf you would like to test your changes locally, Amethyst can be built using [`fastlane`](https://github.com/fastlane/fastlane). Just run the command `fastlane` in the root folder, and the app will be available at `./build/Amethyst.app`. (You may need to provision the app under \"Signing & Capabilities\" in XCode first.)\n\n## Contributing\n\nIf you'd like to contribute please branch off of the `development` branch and open pull requests against it rather than `master`. Otherwise just try to stick to the general style of the code.\n\n## Donating\n\nAmethyst is free and always will be. That said, a couple of people have expressed their desire to donate money in appreciation. Given the current political climate I would recommend donating to one of these organizations instead:\n\n* [American Civil Liberties Union](https://www.aclu.org/)\n* [Planned Parenthood](https://www.plannedparenthood.org/)\n* [Southern Poverty Law Center](https://www.splcenter.org/)\n* [National Resources Defense Council](https://www.nrdc.org/)\n* [International Refugee Assistance Project](https://refugeerights.org/)\n* [NAACP Legal Defense Fund](https://www.naacpldf.org/)\n* [The Trevor Project](https://www.thetrevorproject.org/)\n* [Mexican American Legal Defense Fund](https://www.maldef.org/)\n* [ProPublica](https://www.propublica.org/)\n\nAnd a bunch of technology-oriented ones:\n\n* [National Center for Women & Information Technology](https://ncwit.org/about-ncwit/donate/)\n* [girls who code](https://girlswhocode.com/get-involved/)\n* [Trans*H4CK](https://www.transhack.org/sponsorship/)\n* [Black Girls CODE](https://wearebgc.org/donate/)\n"
  },
  {
    "path": "docs/configuration-files.md",
    "content": "# Configuration Files\n\nAmethyst will pick up a config file located at `~/.amethyst.yml` or `~/.config/amethyst/amethyst.yml` in this order. A sample can be found at [/.amethyst.sample.yml](../.amethyst.sample.yml)\n\n## Configuration Keys\n\n| Key | Description |\n| -- | -- |\n| `layouts` | Ordered list of layouts to use by layout key (default tall, wide, fullscreen, and column). |\n| `mod1` | First mod (default option + shift). |\n| `mod2` | Second mod (default option + shift + control). |\n| `mod3` | Third mod (not used by default). |\n| `mod4` | Fourth mod (not used by default). |\n| `window-max-count` | The max number of windows that may be visible on a screen at one time before additional windows are minimized. A value of 0 disables the feature. |\n| `window-margins` | Boolean flag for whether or not to add margins between and around windows (default `false`). |\n| `smart-window-margins` | Boolean flag for whether or not to set window margins if there is only one window on the screen, assuming window margins are enabled (default `false`). |\n| `window-margin-size` | The size of the margins between and around windows (in px, default `0`). |\n| `window-minimum-height` | The smallest height that a window can be sized to regardless of its layout frame (in px, default `0`). |\n| `window-minimum-width` | The smallest width that a window can be sized to regardless of its layout frame (in px, default `0`) |\n| `floating` | List of bundle identifiers for applications to either be automatically floating or automatically tiled based on `floating-is-blacklist` (default `[]`). |\n| `floating-is-blacklist` | Boolean flag determining behavior of the `floating` list. `true` if the applications should be floating and all others tiled. `false` if the applications should be tiled and all others floating (default `true`). |\n| `ignore-menu-bar` | `true` if screen frames should exclude the status bar. `false` if the screen frames should include the status bar (default `false`). |\n| `float-small-windows` | `true` if windows smaller than the `small-window-size` threshold should be floating by default (default `true`). |\n| `small-window-size` | Pixel threshold for `float-small-windows`. Windows with both width and height below this value are considered small (in px, default `500`). |\n| `mouse-follows-focus` | `true` if the mouse should move position to the center of a window when it becomes focused (default `false`). Note that this is largely incompatible with `focus-follows-mouse`. |\n| `focus-follows-mouse` | `true` if the windows underneath the mouse should become focused as the mouse moves (default `false`). Note that this is largely incompatible with `mouse-follows-focus` |\n| `mouse-swaps-windows` | `true` if dragging and dropping windows on to each other should swap their positions (default `false`). |\n| `mouse-resizes-windows` | `true` if changing the frame of a window with the mouse should update the layout to accommodate the change (default `false`). Note that not all layouts will be able to respond to the change. |\n| `enables-layout-hud` | `true` to display the name of the layout when a new layout is selected (default `true`). |\n| `enables-layout-hud-on-space-change` | `true` to display the name of the layout when moving to a new space (default `true`). |\n| `enables-window-count-hud` | `true` to display notifications when window max count changes (default `false`). |\n| `use-canary-build` | `true` to get updates to beta versions of the software (default `false`). |\n| `new-windows-to-main` | `true` to insert new windows into the first position and `false` to insert new windows into the last position (default `false`). |\n| `follow-space-thrown-windows` | `true` to automatically move to a space when throwing a window to it (default `true`). | \n| `window-resize-step` | The integer percentage of the screen dimension to increment and decrement main pane ratios by (default `5`). |\n| `screen-padding-left` | Padding to apply between windows and the left edge of the screen (in px, default `0`). |\n| `screen-padding-right` | Padding to apply between windows and the right edge of the screen (in px, default `0`). |\n| `screen-padding-top` | Padding to apply between windows and the top edge of the screen (in px, default `0`). |\n| `screen-padding-bottom` | Padding to apply between windows and the bottom edge of the screen (in px, default `0`).\n| `restore-layouts-on-launch` | `true` to maintain layout state across application executions (default `true`). |\n| `debug-layout-info` | `true` to display some optional debug information in the layout HUD (default `false`). |\n| `disable-padding-on-builtin-display` |  `true` to disable screen padding on in-built display (default `false`). |\n| `hide-menu-bar-icon` | `true` to hide the menu bar icon (default `false`). |\n\n## Commands\n\nCommands are defined at the root of the config file, as either an object with `mod` and `key` values to customize the command or is `false` to entirely disable it.\n\n| Key | Description |\n| --- | ----------- |\n| `mod` | The modifier to use, either `mod1`, `mod2`, `mod3` or `mod4`. |\n| `key` | The key on the keyboard to use. |\n\n### Mods\n\nA mod is a list of keyboard modifiers. Namely, `option`, `control`, `shift`, and `command`.\n\n### Command Keys\n\n| Command | Description |\n| ------- | ------------|\n| `cycle-layout` | Move to the next layout in the list. |\n| `cycle-layout-backward` | Move to the previous layout in the list. |\n| `shrink-main` | Shrink the main pane by a percentage of the screen dimension as defined by `window-resize-step`. Note that not all layouts respond to this command. |\n| `expand-main` | Expand the main pane by a percentage of the screen dimension as defined by `window-resize-step`. Note that not all layouts respond to this command. |\n| `increase-main` | Increase the number of windows in the main pane. Note that not all layouts respond to this command. |\n| `decrease-main` | Decrease the number of windows in the main pane. Note that not all layouts respond to this command. |\n| `increase-window-max-count` | Increase the maximum number of windows allowed on screen before additional windows are minimized. |\n| `decrease-window-max-count` | Decrease the maximum number of windows allowed on screen before additional windows are minimized. |\n| `command1` | General purpose command for custom layouts. Functionality is layout-dependent. |\n| `command2` | General purpose command for custom layouts. Functionality is layout-dependent. |\n| `command3` | General purpose command for custom layouts. Functionality is layout-dependent. |\n| `command4` | General purpose command for custom layouts. Functionality is layout-dependent. |\n| `focus-ccw` | Focus the next window in the list going counter-clockwise. |\n| `focus-cw` | Focus the next window in the list going clockwise. |\n| `focus-main` | Focus the main window in the list. |\n| `focus-screen-ccw` | Focus the next screen in the list going counter-clockwise. |\n| `focus-screen-cw` | Focus the next screen in the list going clockwise. |\n| `swap-screen-ccw` | Move the currently focused window onto the next screen in the list going counter-clockwise. |\n| `swap-screen-cw` | Move the currently focused window onto the next screen in the list going clockwise. |\n| `swap-ccw` | Swap the position of the currently focused window with the next window in the list going counter-clockwise. |\n| `swap-cw` | Swap the position of the currently focused window with the next window in the list going clockwise. |\n| `swap-main` | Swap the position of the currently focused window with the main window in the list. |\n| `focus-screen-n` | Move focus to the n-th screen in the list; e.g., `focus-screen-3` will move mouse focus to the 3rd screen. Note that the main window in the given screen will be focused. |\n| `throw-screen-n` | Move the currently focused window to the n-th screen; e.g., `throw-screen-3` will move the window to the 3rd screen. |\n| `throw-space-n` | Move the currently focused window to the n-th space; e.g., `throw-space-3` will move the window to the 3rd space. |\n| `throw-space-left` | Move the currently focused window to the space to the left. |\n| `throw-space-right` | Move currently the focused window to the space to the right. |\n| `toggle-float` | Toggle the floating state of the currently focused window; i.e., if it was floating make it tiled and if it was tiled make it floating. |\n| `display-current-layout` | Display the layout HUD with the current layout on each screen. |\n| `toggle-tiling` | Turn on or off tiling entirely. |\n| `enable-tiling` | Turn on tiling. |\n| `disable-tiling` | Turn off tiling. |\n| `reevaluate-windows` | Rerun the current layout's algorithm. |\n| `toggle-focus-follows-mouse` | Turn on or off `focus-follows-mouse`. |\n| `relaunch-amethyst` | Automatically quit and reopen Amethyst. |\n\n### Layout Selection\n\nAmethyst supports defining shortcuts for selecting specific layouts directly. They take the form of `select-${layout_key}-layout`. For example, defining the command `select-tall-layout` will define a shortcut that when used will switch directly to the Tall layout. Note, this works for custom layouts as well.\n"
  },
  {
    "path": "docs/custom-layouts.md",
    "content": "# Custom Layouts (beta)\n\nAmethyst supports implementing custom layouts via JavaScript.\n\n## Installing\n\nLayouts are located in `~/Library/Application Support/Amethyst/Layouts/`. This directory is automatically created when Amethyst is first launched. JavaScript files in this directory will automatically be picked up and keyed by the name of the file; e.g., `my-cool-layout.js` will be available as a layout with the key `my-cool-layout`.\n\nAt the moment, files must be manually moved to this directory. There is no way to import them through the app.\n\n## Defining a Layout\n\nAt the root of the file you must define a function named `layout`. This function should return an object with the following properties.\n\n### Layout Properties\n\n#### `name`\n\nA string defining the name of the layout. If no name is specified it will default to the layout key.\n\n#### `initialState`\n\nAn object defining any initial state to be tracked by the layout.\n\n#### `commands`\n\nAn object defining the commands the layout responds to. There are four available custom commands keyed as `command1`, `command2`, `command3`, and `command4`, in addition to paned layout commands keyed as `expandMain`, `shrinkMain`, `increaseMain`, and `decreaseMain`. Commands are objects with a `description` string to describe what the command does and an `updateState` function.\n\nThe `updateState` function takes two arguments—`state` and `focusedWindowID`—and must return a new state object.\n\n* `state`: the current layout state\n* `focusedWindowID`: the currently focused window\n\n#### `extends`\n\nThe key for a layout that the custom layout extends. Each call to `getFrameAssignments` will include the frames determined by the extended layout for reference. There are some limitations to this extension—you cannot affect the state of the extended layout, for example—but this is useful for small modifications to existing native layout algorithms.\n\n#### `getFrameAssignments`\n\nA function that takes four arguments—`windows`, `screenFrame`, `state`, and `extendedFrames`—and returns a mapping of window ids to window frames.\n\n* `windows`: the list of active windows on the screen\n* `screenFrame`: the frame of the screen containing the layout\n* `state`: the current layout state\n* `extendedFrames`: the frames that are inherited from the extended layout if any is defined\n\nThe return should be an object with _new_ frames keyed by the window id.\n\n#### `updateWithChange`\n\nA function that takes two arguments—`change` and `state`—and must return a new layout state based on the provided change.\n\n* `change`: the particular change the layout needs to respond to.\n\n#### `recommendMainPaneRatio`\n\nA function that takes two arguments—`ratio` and `state`—and must return a new layout state based on the recommended ratio.\n\n* `ratio`: the ratio recommended for the layout based on windows being resized by mouse controls.\n\n### Mouse Resizing\n\nAmethyst supports changing the relative ratios of windows when changing the size of windows by dragging them with the cursor. By default, these ratios are recommended by calling the `recommendMainPaneRatio` layout property, and happen on the horizontal axis. When the window is resized, the system determines what ratio is appropriate for the new width given the dimensions of the screen it is on. These values are clamped to [0, 1].\n\nTo scale along a different axis, you can specify the `unconstrainedDimension` and `isMain` properties of each window's frame. The dimension determines the axis along which window frame changes will cause recommended ratio changes, and the `isMain` property determines which part of the ratio the window applies to.\n\nNote that currently the recommended ratio is global to the layout and not specific to a given window, so it is not particularly meaningful to specify multiple `unconstrainedDimension` values among frames.\n\n### Common Structures\n\n#### Windows\n\nA window is an object with three properties.\n\n* `id`: an opaque identifier for referencing the window both within `getFrameAssignments` and across the layout state\n* `frame`: the current frame of the window in the screen space\n* `isFocused`: boolean for whether or not the window is currently focused\n\n#### Frames\n\nA frame is an object with four required properties and two optional properties.\n\n* `x`: x-coordinate in the screen space\n* `y`: y-coordinate in the screen space\n* `width`: pixel width\n* `height`: pixel height\n* (optional) `isMain`: boolean indicating whether the window is in the main pane (default: `true`)\n* (optional) `unconstrainedDimension`: a string indicating on which axis the window is able to be resized via mouse (values: `horizontal`, `vertical`; default: `horizontal`)\n\nNote that frames are in a global space, not relative to a given screen.\n\n#### Changes\n\nA change represents the outcome of an event in the system. It is an object with up to two properties.\n\n* `change`: the string key of the type of event (see below)\n* `windowID`: the window id for relevant changes\n* `otherWindowID`: the second window id for relevant changes\n\nCurrent changes are:\n\n* `\"add\"`: a window has been added to tracking\n    * Has a `windowID` for the new window\n* `\"remove\"`: a window has been removed from tracking\n    * Has a `windowID` for the removed window\n* `\"focus_changed\"`: the currently focused window has changed\n    * Has a `windowID` for the newly focused window\n* `\"window_swap\"`: two windows have been swapped in position\n    * Has a `windowID` for the first window and an `otherWindowID` for the second window\n* `\"application_activate\"`: an application has been activated\n    * No parameters\n* `\"application_deactivate\"`: an application has been deactivated\n    * No parameters\n* `\"space_change\"`: the current space on a screen has changed\n    * No parameters\n* `\"layout_change\"`: the layout of a screen changed\n    * No parameters\n* `\"unknown\"`: an unknown event\n    * No parameters\n\n## Examples\n\nThere are several layouts defined for automated tests that can serve as examples. They are in [AmethystTests/Model/CustomLayouts/](../AmethystTests/Model/CustomLayouts/).\n"
  },
  {
    "path": "docs/troubleshooting.md",
    "content": "# Troubleshooting\n\n## Nothing is working!\n\nHere are some common problems and their solutions.\n\n### \"Always float\"\n\nAmethyst has the option to float everything by default until the user manually intervenes. When this mode is unintentionally enabled it can appear that nothing is working. The option is located in settings in the Floating tab and configured with the `floating-is-blacklist` key in a configuration file.\n\nIf you have not intentionally enabled this make sure that the option in the Floating tab says \"Automatically float applications listed\" as in this screenshot.\n\n<img src=\"./images/floating-preferences.png\" width=\"50%\">\n\n## One application isn't working!\n\n### \"Assign To All Desktops\"\n\nmacOS has the option to assign an application specifically to no Desktops, one Desktop, or all Desktops. Amethyst does not handle the last option very well.\n\nTo change this setting you can right click (or control click or whatever gesture you may have associated with right click) on the application icon in the Dock. Under Options there is an Assign To section. See the screenshot below for reference.\n\n<img src=\"./images/desktop-assignment.png\" width=\"50%\">\n"
  },
  {
    "path": "docs/window-limit.md",
    "content": "# Window Limit \n\n## How To Enable\n\nIn the General tab of Amethyst's preferences, there is a Maximum Window Count field. Setting this field to a value greater than 0 will enable the feature.\n\n## Behavior\n\n* Windows set to float using the keyboard shortcut will not be minimized.\n* Main windows will not be minimized. Enable ‘Send new windows to main pane’ to have the oldest window cycle out. Disable the setting to exclude main panes, which will act as a persistent workspace alongside other windows.\n* The window limit applies on a per-screen basis.\n* Disabling Amethyst will disable window limits. Use the Floating layout to apply window limits without any window tiling.\n\n## Recommendations\n\nIt is recommended to enable macOS's setting under System Preferences's Dock pane to minimize windows into their dock icon. This will avoid Dock clutter building up while using the feature.\n\nTo create an iPad-like experience:\n* Set window limit to 2, and have just fullscreen & 1 \"paned layout\" enabled. Cycle between layouts to toggle between fullscreen & split view.\n* Enable \"Swap windows using mouse\" & \"Resize windows using mouse\".\n* Float windows to put them in slide-over mode.\n\n##　Limitations\n\n* The window limit may not be enforced when windows are moved between screens. Creating a new window will correct the issue.  \n"
  },
  {
    "path": "exportOptions.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>method</key>\n\t<string>developer-id</string>\n\t<key>teamID</key>\n\t<string>82P2XLB4UH</string>\n\t<key>compileBitcode</key>\n\t<false/>\n\t<key>destination</key>\n\t<string>export</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "fastlane/Appfile",
    "content": "# app_identifier(\"[[APP_IDENTIFIER]]\") # The bundle identifier of your app\n# apple_id(\"[[APPLE_ID]]\") # Your Apple email address\n\n\n# For more information about the Appfile, see:\n#     https://docs.fastlane.tools/advanced/#appfile\n"
  },
  {
    "path": "fastlane/Fastfile",
    "content": "# This file contains the fastlane.tools configuration\n# You can find the documentation at https://docs.fastlane.tools\n#\n# For a list of all available actions, check out\n#\n#     https://docs.fastlane.tools/actions\n#\n# For a list of all available plugins, check out\n#\n#     https://docs.fastlane.tools/plugins/available-plugins\n#\n\n# Uncomment the line if you want fastlane to automatically update itself\n# update_fastlane\n\ndefault_platform(:mac)\n\nlane :mac do\n    build_mac_app\n    xcodebuild(\n        export_archive: true,\n        archive_path: \"./build/Amethyst.xcarchive\",\n        export_path: \"./build\",\n        export_options_plist: \"./exportOptions.plist\"\n    )\nend\n"
  },
  {
    "path": "fastlane/Gymfile",
    "content": "archive_path(\"./build/Amethyst\")\nscheme(\"Amethyst\")\noutput_directory(\"./build\")\n"
  },
  {
    "path": "fastlane/README.md",
    "content": "fastlane documentation\n----\n\n# Installation\n\nMake sure you have the latest version of the Xcode command line tools installed:\n\n```sh\nxcode-select --install\n```\n\nFor _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane)\n\n# Available Actions\n\n### mac\n\n```sh\n[bundle exec] fastlane mac\n```\n\n\n\n----\n\nThis README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.\n\nMore information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools).\n\nThe documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools).\n"
  },
  {
    "path": "privacy-policy.md",
    "content": "Amethyst Privacy Policy\n-----------------------\n\nAmethyst does not collect any personal information and does not transmit any information over a network."
  }
]