Full Code of ianyh/Amethyst for AI

development 9d87018cedc2 cached
146 files
900.9 KB
216.0k tokens
9 symbols
1 requests
Download .txt
Showing preview only (951K chars total). Download the full file or copy to clipboard to get everything.
Repository: ianyh/Amethyst
Branch: development
Commit: 9d87018cedc2
Files: 146
Total size: 900.9 KB

Directory structure:
gitextract_92so_qqv/

├── .amethyst.sample.yml
├── .eslintrc.yml
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .hound.yml
├── .swiftlint.yml
├── Amethyst/
│   ├── Amethyst-Bridging-Header.h
│   ├── Amethyst-Info.plist
│   ├── Amethyst.entitlements
│   ├── AmethystDebug.entitlements
│   ├── AppDelegate.swift
│   ├── Base.lproj/
│   │   └── MainMenu.xib
│   ├── Categories/
│   │   ├── NSRunningApplication+Manageable.swift
│   │   └── NSTableView+Amethyst.swift
│   ├── Debug/
│   │   ├── AppsInfo.swift
│   │   ├── DebugInfo.swift
│   │   ├── ScreensInfo.swift
│   │   └── WindowsInfo.swift
│   ├── Events/
│   │   └── HotKeyManager.swift
│   ├── Images.xcassets/
│   │   ├── 123.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── amethyst.imageset/
│   │   │   └── Contents.json
│   │   ├── dots.and.line.vertical.and.cursorarrow.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-statusitem-disabled.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-statusitem.imageset/
│   │   │   └── Contents.json
│   │   ├── macwindow.on.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── text.and.command.macwindow.imageset/
│   │   │   └── Contents.json
│   │   ├── uiwindow.split.2x1.imageset/
│   │   │   └── Contents.json
│   │   └── waveform.path.ecg.rectangle.imageset/
│   │       └── Contents.json
│   ├── Layout/
│   │   ├── Layout.swift
│   │   ├── Layouts/
│   │   │   ├── BinarySpacePartitioningLayout.swift
│   │   │   ├── ColumnLayout.swift
│   │   │   ├── CustomLayout.swift
│   │   │   ├── FloatingLayout.swift
│   │   │   ├── FourColumnLayout.swift
│   │   │   ├── FullscreenLayout.swift
│   │   │   ├── RowLayout.swift
│   │   │   ├── TallLayout.swift
│   │   │   ├── TallRightLayout.swift
│   │   │   ├── ThreeColumnLayout.swift
│   │   │   ├── TwoPaneLayout.swift
│   │   │   ├── TwoPaneRightLayout.swift
│   │   │   ├── WideLayout.swift
│   │   │   └── WidescreenTallLayout.swift
│   │   └── ReflowOperation.swift
│   ├── Managers/
│   │   ├── AppManager.swift
│   │   ├── FocusFollowsMouseManager.swift
│   │   ├── FocusTransitionCoordinator.swift
│   │   ├── HotKeyRegistrar.swift
│   │   ├── LayoutType.swift
│   │   ├── LogManager.swift
│   │   ├── ScreenManager.swift
│   │   ├── Screens.swift
│   │   ├── WindowManager.swift
│   │   ├── WindowTransitionCoordinator.swift
│   │   └── Windows.swift
│   ├── Model/
│   │   ├── Application.swift
│   │   ├── ApplicationEventHandler.swift
│   │   ├── ApplicationObservation.swift
│   │   ├── CGInfo.swift
│   │   ├── Change.swift
│   │   ├── MouseState.swift
│   │   ├── Reliability.swift
│   │   ├── Screen.swift
│   │   ├── Space.swift
│   │   ├── Window.swift
│   │   └── WindowsInformation.swift
│   ├── Preferences/
│   │   ├── DebugPreferencesViewController.swift
│   │   ├── DebugPreferencesViewController.xib
│   │   ├── FloatingPreferencesViewController.swift
│   │   ├── FloatingPreferencesViewController.xib
│   │   ├── GeneralPreferencesViewController.swift
│   │   ├── GeneralPreferencesViewController.xib
│   │   ├── LayoutsPreferencesViewController.swift
│   │   ├── LayoutsPreferencesViewController.xib
│   │   ├── MousePreferencesViewController.swift
│   │   ├── MousePreferencesViewController.xib
│   │   ├── ShortcutsPreferencesListItemView.swift
│   │   ├── ShortcutsPreferencesViewController.swift
│   │   ├── ShortcutsPreferencesViewController.xib
│   │   └── UserConfiguration.swift
│   ├── View/
│   │   ├── LayoutNameWindow.swift
│   │   ├── LayoutNameWindow.xib
│   │   ├── LayoutNameWindowController.swift
│   │   └── PreferencesWindow.swift
│   ├── default.amethyst
│   ├── en.lproj/
│   │   ├── Credits.rtf
│   │   └── InfoPlist.strings
│   └── main.swift
├── Amethyst.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── swiftpm/
│   │           └── Package.resolved
│   └── xcshareddata/
│       └── xcschemes/
│           ├── Amethyst Debug CLI.xcscheme
│           └── Amethyst.xcscheme
├── Amethyst.xctestplan
├── Amethyst.xcworkspace/
│   ├── contents.xcworkspacedata
│   └── xcshareddata/
│       ├── IDEWorkspaceChecks.plist
│       └── swiftpm/
│           └── Package.resolved
├── AmethystTests/
│   ├── AmethystTests-Bridging-Header.h
│   ├── Helpers/
│   │   ├── FrameAssignmentVerification.swift
│   │   └── TestBundle.swift
│   ├── Info.plist
│   ├── Model/
│   │   ├── CustomLayouts/
│   │   │   ├── extended.js
│   │   │   ├── fullscreen.js
│   │   │   ├── null.js
│   │   │   ├── recommended-main-pane-ratio.js
│   │   │   ├── static-ratio-tall-native-commands.js
│   │   │   ├── static-ratio-tall.js
│   │   │   ├── subset.js
│   │   │   ├── undefined.js
│   │   │   └── uniform-columns.js
│   │   ├── TestScreen.swift
│   │   └── TestWindow.swift
│   └── Tests/
│       ├── Categories/
│       │   └── SIWindow+AmethystTests.swift
│       ├── Configuration/
│       │   └── UserConfigurationTests.swift
│       ├── Layout/
│       │   ├── BinarySpacePartitioningLayoutTests.swift
│       │   ├── ColumnLayoutTests.swift
│       │   ├── CustomLayoutTests.swift
│       │   ├── FloatingLayoutTests.swift
│       │   ├── FullscreenLayoutTests.swift
│       │   ├── RowLayoutTests.swift
│       │   ├── TallLayoutTests.swift
│       │   ├── TallRightLayoutTests.swift
│       │   ├── ThreeColumnLayoutTests.swift
│       │   ├── TwoPaneLayoutTests.swift
│       │   ├── WideLayoutTests.swift
│       │   └── WidescreenTallLayoutTests.swift
│       └── Managers/
│           ├── HotKeyManagerTests.swift
│           └── ScreenManagerTests.swift
├── Brewfile
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── configuration-files.md
│   ├── custom-layouts.md
│   ├── troubleshooting.md
│   └── window-limit.md
├── exportOptions.plist
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   ├── Gymfile
│   └── README.md
└── privacy-policy.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .amethyst.sample.yml
================================================
# Default settings for Amethyst
# Repo: `https://github.com/ianyh/Amethyst`
#
# Note due to issue 1419 (https://github.com/ianyh/Amethyst/issues/1419) some
# config values may conflict and not work if they are the same as the default
# values for Amethyst. You can see these values on GitHub at
# https://github.com/ianyh/Amethyst/blob/development/Amethyst/default.amethyst
# If you're experiencing conflicts and the settings are the same as the default,
# comment out the commands in this file.
#
# Move this file to: `~/.amethyst.yml`
# In order to register changes restart Amethyst.
# If you experience issues pulling in the changes you can also quit Amethyst and run: `defaults delete com.amethyst.Amethyst.plist`
# This removes the current preferences and causes Amethyst to restart with default preferences and pull configs from this file.

# layouts - Ordered list of layouts to use by layout key (default tall, wide, fullscreen, and column).
layouts:
  - tall
  - fullscreen
  # - tall-right
  - wide
  # - two-pane
  # - middle-wide
  # - 3column-left
  # - middle-wide # The legacy name of "3column-middle"
  # - 3column-right
  # - 4column-left
  # - 4column-right
  - column
  # - row
  # - floating
  # - widescreen-tall
  # - widescreen-tall-right
  # - bsp

# First mod (default option + shift).
mod1:
  - option
  - shift
  # - control
  # - command

# Second mod (default option + shift + control).
mod2:
  - option
  - shift
  - control
  # - command

# Commands:
# special key values
# space
# enter
# up
# right
# down
# left

# special characters require quotes
# '.'
# ','

# Move to the next layout in the list.
cycle-layout:
  mod: mod1
  key: space

# Move to the previous layout in the list.
cycle-layout-backward:
  mod: mod2
  key: space

# 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.
shrink-main:
  mod: mod1
  key: h

# 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.
expand-main:
  mod: mod1
  key: l

# Increase the number of windows in the main pane. Note that not all layouts respond to this command.
increase-main:
  mod: mod1
  key: ','

# Decrease the number of windows in the main pane. Note that not all layouts respond to this command.
decrease-main:
  mod: mod1
  key: '.'

# General purpose command for custom layouts. Functionality is layout-dependent.
# command1:
#   mod: <NONE>
#   key: <NONE>
# General purpose command for custom layouts. Functionality is layout-dependent.
# command2:
#   mod: <NONE>
#   key: <NONE>
# General purpose command for custom layouts. Functionality is layout-dependent.
# command3:
#   mod: <NONE>
#   key: <NONE>
# General purpose command for custom layouts. Functionality is layout-dependent.
# command4:
#   mod: <NONE>
#   key: <NONE>

# Focus the next window in the list going counter-clockwise.
focus-ccw:
  mod: mod1
  key: j

# Focus the next window in the list going clockwise.
focus-cw:
  mod: mod1
  key: k

# Focus the main window in the list.
focus-main:
  mod: mod1
  key: m

# Focus the next screen in the list going counter-clockwise.
focus-screen-ccw:
  mod: mod1
  key: p

# Focus the next screen in the list going clockwise.
focus-screen-cw:
  mod: mod1
  key: n

# Move the currently focused window onto the next screen in the list going counter-clockwise.
swap-screen-ccw:
  mod: mod2
  key: h

# Move the currently focused window onto the next screen in the list going clockwise.
swap-screen-cw:
  mod: mod2
  key: l

# Swap the position of the currently focused window with the next window in the list going counter-clockwise.
swap-ccw:
  mod: mod2
  key: j

# Swap the position of the currently focused window with the next window in the list going clockwise.
swap-cw:
  mod: mod2
  key: k

# Swap the position of the currently focused window with the main window in the list.
swap-main:
  mod: mod1
  key: enter

# 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.
#focus-screen-n:
# focus-screen-<screen-number>:
#   mod: mod1
#   key: y
# Move the currently focused window to the n-th screen; e.g., throw-screen-3 will move the window to the 3rd screen.
# throw-screen-n:
# throw-screen-<screen-number>:
#   mod: mod1
#   key: u
# Move the currently focused window to the n-th space; e.g., throw-space-3 will move the window to the 3rd space.
# throw-space-<screen-number>:
#   mod: mod1
#   key: i

# Select tall layout
select-tall-layout:
  mod: mod1
  key: a

# Select wide layout
select-wide-layout:
  mod: mod1
  key: s

# Select fullscreen layout
select-fullscreen-layout:
  mod: mod1
  key: d

# Select column layout
select-column-layout:
  mod: mod1
  key: f

# Move the currently focused window to the space to the left.
throw-space-left:
  mod: mod2
  key: left

# Move currently the focused window to the space to the right.
throw-space-right:
  mod: mod2
  key: right

# 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.
toggle-float:
  mod: mod1
  key: t

# Display the layout HUD with the current layout on each screen.
display-current-layout:
  mod: mod1
  key: i

# Turn on or off tiling entirely.
toggle-tiling:
  mod: mod2
  key: t

# Turn on tiling.
# enable-tiling:
#   mod: mod2
#   key: <NONE>

# Turn off tiling.
# disable-tiling:
#   mod: mod2
#   key: <NONE>

# Rerun the current layout's algorithm.
reevaluate-windows:
  mod: mod1
  key: z

# Turn on or off focus-follows-mouse.
toggle-focus-follows-mouse:
  mod: mod2
  key: x

# Automatically quit and reopen Amethyst.
relaunch-amethyst:
  mod: mod2
  key: z

# disable screen padding on builtin display
disable-padding-on-builtin-display: false

# Boolean flag for whether or not to add margins between windows (default false).
window-margins: false

# 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).
smart-window-margins: false

# # Add 10px margin between windows
# window-margins: true
# window-margin-size: 5
# The size of the margins between windows (in px, default 0).
window-margin-size: 0

# 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.
window-max-count: 0

# The smallest height that a window can be sized to regardless of its layout frame (in px, default 0).
window-minimum-height: 0

# The smallest width that a window can be sized to regardless of its layout frame (in px, default 0)
window-minimum-width: 0

# List of bundle identifiers for applications to either be automatically floating or automatically tiled based on floating-is-blacklist (default []).
floating: []

# 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).
floating-is-blacklist: true

# true if screen frames should exclude the status bar. false if the screen frames should include the status bar (default false).
ignore-menu-bar: false

# true if menu bar icon should be hidden (default false).
hide-menu-bar-icon: false

# true if windows smaller than the small-window-size threshold should be floating by default (default true)
float-small-windows: true

# Pixel threshold for float-small-windows. Windows with both width and height below this value are considered small (in px, default 500).
small-window-size: 500

# 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.
mouse-follows-focus: false

# 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
focus-follows-mouse: false

# true if dragging and dropping windows on to each other should swap their positions (default false).
mouse-swaps-windows: false

# 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.
mouse-resizes-windows: false

# true to display the name of the layout when a new layout is selected (default true).
enables-layout-hud: true

# true to display the name of the layout when moving to a new space (default true).
enables-layout-hud-on-space-change: true

# true to get updates to beta versions of the software (default false).
use-canary-build: false

# true to insert new windows into the first position and false to insert new windows into the last position (default false).
new-windows-to-main: false

# true to automatically move to a space when throwing a window to it (default true).
follow-space-thrown-windows: true

# The integer percentage of the screen dimension to increment and decrement main pane ratios by (default 5).
window-resize-step: 5

# Padding to apply between windows and the left edge of the screen (in px, default 0).
screen-padding-left: 0

# Padding to apply between windows and the right edge of the screen (in px, default 0).
screen-padding-right: 0

# Padding to apply between windows and the top edge of the screen (in px, default 0).
screen-padding-top: 0

# Padding to apply between windows and the bottom edge of the screen (in px, default 0).
screen-padding-bottom: 0

# true to maintain layout state across application executions (default true).
restore-layouts-on-launch: true

# true to display some optional debug information in the layout HUD (default false).
debug-layout-info: false


================================================
FILE: .eslintrc.yml
================================================
---
    env:
        es6: true
    parserOptions:
        ecmaVersion: 9


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**Applications:**
What applications are involved?

**To Reproduce**
Steps to reproduce the behavior:

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Versions:**
 - macOS: 
 - Amethyst: 
 
**Debug Info**
```
$ /Applications/Amethyst.app/Contents/MacOS/Amethyst --debug-info [--include-apps]
```
Note: `--include-apps` will list your manageable applications, but is optional if you don't want to list that.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


================================================
FILE: .github/workflows/tests.yml
================================================
name: Tests

on:
  push:
    branches: [ development ]
  pull_request:

jobs:
  build:
    name: Build and run unit tests
    runs-on: macos-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Dependencies
        run: |
          brew bundle
      - name: Test
        run: |
          set -o pipefail && xcodebuild -workspace Amethyst.xcworkspace -scheme Amethyst clean test | xcbeautify



================================================
FILE: .gitignore
================================================
# Xcode
.DS_Store
*/build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
profile
*.moved-aside
DerivedData
.idea/
*.hmap
Pods
build
crashlytics_api_key
crashlytics_app_key
Carthage
AMKeys.h
fastlane/report.xml
*profraw

# Homebrew
Brewfile.lock.json



================================================
FILE: .hound.yml
================================================
swiftlint:
    config_file: .swiftlint.yml

eslint:
    enabled: true
    config_file: .eslintrc.yml


================================================
FILE: .swiftlint.yml
================================================
disabled_rules:
    - function_body_length
    - closing_brace
    - statement_position
    - force_cast
    - force_try
    - no_space_in_method_call
    - file_length
    - type_body_length
included:
    - Amethyst
    - AmethystTests
line_length:
    warning: 200
    ignores_comments: true
cyclomatic_complexity: 15
large_tuple: 3
nesting:
    type_level: 2
identifier_name:
    excluded:
        - id


================================================
FILE: Amethyst/Amethyst-Bridging-Header.h
================================================


================================================
FILE: Amethyst/Amethyst-Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(MARKETING_VERSION)</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>LSApplicationCategoryType</key>
	<string>public.app-category.utilities</string>
	<key>LSMinimumSystemVersion</key>
	<string>${MACOSX_DEPLOYMENT_TARGET}</string>
	<key>LSUIElement</key>
	<true/>
	<key>NSMainNibFile</key>
	<string>MainMenu</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
	<key>SUCanaryFeedURL</key>
	<string>https://ianyh.com/amethyst/canary-appcast.xml</string>
	<key>SUFeedURL</key>
	<string>https://ianyh.com/amethyst/appcast.xml</string>
</dict>
</plist>


================================================
FILE: Amethyst/Amethyst.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>


================================================
FILE: Amethyst/AmethystDebug.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.cs.allow-jit</key>
	<true/>
	<key>com.apple.security.cs.disable-library-validation</key>
	<true/>
</dict>
</plist>


================================================
FILE: Amethyst/AppDelegate.swift
================================================
//
//  AppDelegate.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/8/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import CoreServices
import Foundation
import LoginServiceKit
import RxCocoa
import RxSwift
import Silica
import Sparkle
import SwiftyBeaver

class AppDelegate: NSObject, NSApplicationDelegate {
    static let windowManagerEncodingKey = "EncodedWindowManager"

    @IBOutlet var preferencesWindowController: PreferencesWindowController?

    fileprivate var windowManager: WindowManager<SIApplication>?
    private var hotKeyManager: HotKeyManager<SIApplication>?

    fileprivate var statusItem: NSStatusItem?
    @IBOutlet var statusItemMenu: NSMenu?
    @IBOutlet var versionMenuItem: NSMenuItem?
    @IBOutlet var startAtLoginMenuItem: NSMenuItem?
    @IBOutlet var toggleGlobalTilingMenuItem: NSMenuItem?
    @IBOutlet var layoutsMenuItem: NSMenuItem?

    private var isFirstLaunch = true

    func applicationDidFinishLaunching(_ notification: Notification) {
        #if DEBUG
            log.addDestination(ConsoleDestination())
        #endif

        if CommandLine.arguments.contains("--log") {
            let destination = ConsoleDestination()
            destination.useNSLog = true
            log.addDestination(destination)
        }

        log.info("Logging is enabled")
        log.debug("Debug logging is enabled")

        UserConfiguration.shared.delegate = self
        UserConfiguration.shared.load()

        #if RELEASE
            let appcastURLString = { () -> String? in
                if UserConfiguration.shared.useCanaryBuild() {
                    return Bundle.main.infoDictionary?["SUCanaryFeedURL"] as? String
                } else {
                    return Bundle.main.infoDictionary?["SUFeedURL"] as? String
                }
            }()!

            SUUpdater.shared().feedURL = URL(string: appcastURLString)
        #endif

        preferencesWindowController?.window?.level = .floating

        if let encodedWindowManager = UserDefaults.standard.data(forKey: AppDelegate.windowManagerEncodingKey), UserConfiguration.shared.restoreLayoutsOnLaunch() {
            let decoder = JSONDecoder()
            windowManager = try? decoder.decode(WindowManager<SIApplication>.self, from: encodedWindowManager)
        }

        windowManager = windowManager ?? WindowManager(userConfiguration: UserConfiguration.shared)
        hotKeyManager = HotKeyManager(userConfiguration: UserConfiguration.shared)

        hotKeyManager?.setUpWithWindowManager(windowManager!, configuration: UserConfiguration.shared, appDelegate: self)
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        let version = Bundle.main.infoDictionary?["CFBundleVersion"] as! String
        let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
        let statusItemImage = NSImage(named: "icon-statusitem")
        statusItemImage?.isTemplate = true

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        statusItem?.image = statusItemImage
        statusItem?.menu = statusItemMenu
        statusItem?.highlightMode = true

        let hideMenuBarIcon: Bool = UserConfiguration.shared.hideMenuBarIcon()
        statusItem?.isVisible = !hideMenuBarIcon

        versionMenuItem?.title = "Version \(shortVersion) (\(version))"
        toggleGlobalTilingMenuItem?.title = "Disable"

        startAtLoginMenuItem?.state = (LoginServiceKit.isExistLoginItems(at: Bundle.main.bundlePath) ? .on : .off)

        // Set up status item menu delegate to refresh layouts when main menu is opened
        statusItemMenu?.delegate = self
    }

    func applicationDidBecomeActive(_ notification: Notification) {
        guard !isFirstLaunch else {
            isFirstLaunch = false
            return
        }

        showPreferencesWindow(self)
    }

    func applicationWillTerminate(_ notification: Notification) {
        guard let windowManager = windowManager else {
            return
        }

        do {
            let encoder = JSONEncoder()
            let encodedWindowManager = try encoder.encode(windowManager)
            UserDefaults.standard.set(encodedWindowManager, forKey: AppDelegate.windowManagerEncodingKey)
        } catch {
            log.error("Failed to encode window manager: \(error)")
        }
    }

    @IBAction func toggleStartAtLogin(_ sender: AnyObject) {
        if startAtLoginMenuItem?.state == .off {
            LoginServiceKit.addLoginItems(at: Bundle.main.bundlePath)
        } else {
            LoginServiceKit.removeLoginItems(at: Bundle.main.bundlePath)
        }
        startAtLoginMenuItem?.state = (LoginServiceKit.isExistLoginItems(at: Bundle.main.bundlePath) ? .on : .off)
    }

    @IBAction func toggleGlobalTiling(_ sender: AnyObject) {
        UserConfiguration.shared.tilingEnabled = !UserConfiguration.shared.tilingEnabled
        windowManager?.markAllScreensForReflow()
    }

    @IBAction func resetLayouts(_ sender: AnyObject) {
        UserDefaults.standard.removeObject(forKey: AppDelegate.windowManagerEncodingKey)
        windowManager?.reset()
    }

    @IBAction func relaunch(_ sender: AnyObject) {
        AppManager.relaunch()
    }

    @IBAction func showPreferencesWindow(_ sender: AnyObject) {
        guard let isVisible = preferencesWindowController?.window?.isVisible, !isVisible else {
            return
        }

        preferencesWindowController?.showWindow(nil)
        NSApp.activate(ignoringOtherApps: true)

        presentDotfileWarningIfNecessary()
    }

    @IBAction func checkForUpdates(_ sender: AnyObject) {
        #if RELEASE
            SUUpdater.shared().checkForUpdates(sender)
        #endif
    }

    private func presentDotfileWarningIfNecessary() {
        let shouldWarn = !UserDefaults.standard.bool(forKey: "disable-dotfile-conflict-warning")
        if shouldWarn && UserConfiguration.shared.hasCustomConfiguration() {
            let alert = NSAlert()
            alert.alertStyle = .warning
            alert.messageText = "Warning"
            alert.informativeText = "You have a .amethyst file, which can override in-app preferences. You may encounter unexpected behavior."
            alert.showsSuppressionButton = true
            alert.runModal()

            if alert.suppressionButton?.state == .on {
                UserDefaults.standard.set(true, forKey: "disable-dotfile-conflict-warning")
            }
        }
    }

    private func populateLayoutsMenu() {
        guard let layoutsMenuItem = layoutsMenuItem,
              let submenu = layoutsMenuItem.submenu else {
            return
        }

        // Clear existing items
        submenu.removeAllItems()

        // Get screen manager: try focused screen first, then screen under mouse cursor
        let screenManager: ScreenManager<WindowManager<SIApplication>>? = {
            if let focused = windowManager?.focusedScreenManager() {
                return focused
            }
            // Fallback to screen containing mouse cursor (useful when clicking menu bar)
            let mouseLocation = NSEvent.mouseLocation
            if let nsScreen = NSScreen.screens.first(where: { NSMouseInRect(mouseLocation, $0.frame, false) }) {
                let amScreen = AMScreen(screen: nsScreen)
                return windowManager?.screenManager(for: amScreen)
            }
            return nil
        }()

        guard let screenManager = screenManager else {
            let errorItem = NSMenuItem(title: "Unable to determine current screen", action: nil, keyEquivalent: "")
            errorItem.isEnabled = false
            submenu.addItem(errorItem)
            return
        }

        // Get layouts from the screen manager (not from global config)
        let layouts = screenManager.layoutsInfo

        // Check if no layouts are available and return early
        if layouts.isEmpty {
            let noLayoutsItem = NSMenuItem(title: "No layouts enabled", action: nil, keyEquivalent: "")
            noLayoutsItem.isEnabled = false
            submenu.addItem(noLayoutsItem)
            return
        }

        // Add menu items for each layout in the screen manager
        for layoutInfo in layouts {
            let menuItem = NSMenuItem(title: layoutInfo.name, action: #selector(selectLayout(_:)), keyEquivalent: "")
            menuItem.target = self
            menuItem.representedObject = layoutInfo.key
            menuItem.state = layoutInfo.isSelected ? .on : .off

            submenu.addItem(menuItem)
        }
    }

    @IBAction func selectLayout(_ sender: NSMenuItem) {
        guard let layoutKey = sender.representedObject as? String,
              let windowManager = windowManager,
              let screenManager = windowManager.focusedScreenManager() else {
            return
        }

        screenManager.selectLayout(layoutKey)
        // Menu will be refreshed automatically when next opened via NSMenuDelegate
    }
}

extension AppDelegate: NSWindowDelegate {
    func windowWillClose(_ notification: Notification) {
        windowManager?.preferencesDidClose()
    }
}

extension AppDelegate: NSMenuDelegate {
    func menuWillOpen(_ menu: NSMenu) {
        // Refresh layouts menu when main status item menu is about to open
        if menu == statusItemMenu {
            populateLayoutsMenu()
        }
    }
}

extension AppDelegate: UserConfigurationDelegate {
    func configurationGlobalTilingDidChange(_ userConfiguration: UserConfiguration) {
        var statusItemImage: NSImage?
        if UserConfiguration.shared.tilingEnabled == true {
            statusItemImage = NSImage(named: "icon-statusitem")
            toggleGlobalTilingMenuItem?.title = "Disable Tiling"
        } else {
            statusItemImage = NSImage(named: "icon-statusitem-disabled")
            toggleGlobalTilingMenuItem?.title = "Enable Tiling"
        }
        statusItemImage?.isTemplate = true
        statusItem?.image = statusItemImage
    }

    func configurationAccessibilityPermissionsDidChange(_ userConfiguration: UserConfiguration) {
        windowManager?.reevaluateWindows()
    }
}


================================================
FILE: Amethyst/Base.lproj/MainMenu.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23727" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
    <dependencies>
        <deployment identifier="macosx"/>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23727"/>
        <capability name="Image references" minToolsVersion="12.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
            <connections>
                <outlet property="delegate" destination="494" id="495"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
        <menu id="536">
            <items>
                <menuItem title="Version" enabled="NO" id="bNZ-Ry-Q4Q">
                    <modifierMask key="keyEquivalentModifierMask"/>
                </menuItem>
                <menuItem isSeparatorItem="YES" id="uP9-ch-b7n"/>
                <menuItem title="Disable Tiling" id="rjJ-w4-5Ht">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="toggleGlobalTiling:" target="494" id="3aM-9U-hWx"/>
                    </connections>
                </menuItem>
                <menuItem title="Layouts" id="layouts-menu-item">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" id="layouts-submenu">
                        <items/>
                    </menu>
                </menuItem>
                <menuItem title="Start Amethyst on Login" id="549">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="toggleStartAtLogin:" target="494" id="550"/>
                    </connections>
                </menuItem>
                <menuItem title="Preferences..." id="utO-om-0eu">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="showPreferencesWindow:" target="494" id="DhJ-Jj-71M"/>
                    </connections>
                </menuItem>
                <menuItem isSeparatorItem="YES" id="548"/>
                <menuItem title="Check for Updates..." id="554">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="checkForUpdates:" target="494" id="l9Y-Ct-Bzq"/>
                    </connections>
                </menuItem>
                <menuItem title="Reset Layouts" id="8DH-wG-R1x">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="resetLayouts:" target="494" id="Osy-WG-Dy8"/>
                    </connections>
                </menuItem>
                <menuItem title="Relaunch Amethyst" id="crd-PN-Vy2">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="relaunch:" target="494" id="EeN-oj-Mtv"/>
                    </connections>
                </menuItem>
                <menuItem isSeparatorItem="YES" id="553"/>
                <menuItem title="Quit Amethyst" keyEquivalent="q" id="545">
                    <connections>
                        <action selector="terminate:" target="-3" id="546"/>
                    </connections>
                </menuItem>
            </items>
            <point key="canvasLocation" x="-179" y="92"/>
        </menu>
        <customObject id="494" customClass="AppDelegate" customModule="Amethyst" customModuleProvider="target">
            <connections>
                <outlet property="preferencesWindowController" destination="0fi-bi-huz" id="dfn-JC-QBQ"/>
                <outlet property="startAtLoginMenuItem" destination="549" id="551"/>
                <outlet property="statusItemMenu" destination="536" id="547"/>
                <outlet property="toggleGlobalTilingMenuItem" destination="rjJ-w4-5Ht" id="4Dq-e8-R1M"/>
                <outlet property="versionMenuItem" destination="bNZ-Ry-Q4Q" id="jXC-pY-N2a"/>
                <outlet property="layoutsMenuItem" destination="layouts-menu-item" id="layouts-menu-outlet"/>
            </connections>
        </customObject>
        <customObject id="420" customClass="NSFontManager"/>
        <window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" toolbarStyle="expanded" id="pO4-7U-3HA" customClass="PreferencesWindow" customModule="Amethyst" customModuleProvider="target">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
            <rect key="contentRect" x="446" y="278" width="480" height="270"/>
            <rect key="screenRect" x="0.0" y="0.0" width="1440" height="875"/>
            <view key="contentView" id="IHj-fa-gIc">
                <rect key="frame" x="0.0" y="0.0" width="480" height="260"/>
                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            </view>
            <toolbar key="toolbar" implicitIdentifier="2383369F-98A6-4420-8EFE-319DB9588C53" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconAndLabel" sizeMode="regular" id="5oI-m4-akz">
                <allowedToolbarItems>
                    <toolbarItem implicitItemIdentifier="D4FDE95C-2F3E-4C93-8777-9FBAD1EDB613" explicitItemIdentifier="general" label="General" paletteLabel="General" selectable="YES" id="WV2-G9-AL4">
                        <imageReference key="image" image="text.and.command.macwindow" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="bq3-bL-T4h"/>
                        </connections>
                    </toolbarItem>
                    <toolbarItem implicitItemIdentifier="C7D5B660-7047-4353-8F39-18609BCF05D1" explicitItemIdentifier="layouts" label="Layouts" paletteLabel="Layouts" selectable="YES" id="FsX-hN-WZb" userLabel="Layouts">
                        <imageReference key="image" image="uiwindow.split.2x1" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="mBI-Du-M23"/>
                        </connections>
                    </toolbarItem>
                    <toolbarItem implicitItemIdentifier="04C17AAF-5F6B-422F-8592-E8AA78DEB9C7" explicitItemIdentifier="shortcuts" label="Shortcuts" paletteLabel="Shortcuts" tag="1" selectable="YES" id="j0a-87-Yvz">
                        <imageReference key="image" image="123.rectangle" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="9kO-ms-dfq"/>
                        </connections>
                    </toolbarItem>
                    <toolbarItem implicitItemIdentifier="74318EC6-A099-4964-B13B-6B2B699C7EC3" explicitItemIdentifier="mouse" label="Mouse" paletteLabel="Mouse" selectable="YES" id="dfc-D6-FsG" userLabel="Mouse">
                        <imageReference key="image" image="dots.and.line.vertical.and.cursorarrow.rectangle" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="0vo-hl-zVN"/>
                        </connections>
                    </toolbarItem>
                    <toolbarItem implicitItemIdentifier="A7FE5E71-A3DC-43B8-92B2-5CF107F2DB2F" explicitItemIdentifier="floating" label="Floating" paletteLabel="Floating" tag="-1" selectable="YES" id="QEp-pN-Lz9">
                        <imageReference key="image" image="macwindow.on.rectangle" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="WEg-vF-5FE"/>
                        </connections>
                    </toolbarItem>
                    <toolbarItem implicitItemIdentifier="319E6DB9-3D79-40BF-95A2-B84B5236FF8B" explicitItemIdentifier="debug" label="Debug" paletteLabel="Debug" tag="-1" selectable="YES" id="KUo-2v-ywG">
                        <imageReference key="image" image="waveform.path.ecg.rectangle" catalog="system" symbolScale="large"/>
                        <size key="minSize" width="22" height="22"/>
                        <size key="maxSize" width="22" height="22"/>
                        <connections>
                            <action selector="selectPane:" target="0fi-bi-huz" id="aNs-0B-bfi"/>
                        </connections>
                    </toolbarItem>
                </allowedToolbarItems>
                <defaultToolbarItems>
                    <toolbarItem reference="WV2-G9-AL4"/>
                    <toolbarItem reference="FsX-hN-WZb"/>
                    <toolbarItem reference="j0a-87-Yvz"/>
                    <toolbarItem reference="dfc-D6-FsG"/>
                    <toolbarItem reference="QEp-pN-Lz9"/>
                    <toolbarItem reference="KUo-2v-ywG"/>
                </defaultToolbarItems>
                <connections>
                    <outlet property="delegate" destination="0fi-bi-huz" id="zer-Uh-aco"/>
                </connections>
            </toolbar>
            <connections>
                <outlet property="closeMenuItem" destination="qQS-B4-nPy" id="4g6-N2-DxV"/>
                <outlet property="delegate" destination="494" id="OGF-zj-QHD"/>
            </connections>
            <point key="canvasLocation" x="606" y="152"/>
        </window>
        <customObject id="0fi-bi-huz" userLabel="Preferences Window Controller" customClass="PreferencesWindowController" customModule="Amethyst" customModuleProvider="target">
            <connections>
                <outlet property="window" destination="pO4-7U-3HA" id="9gm-N1-3ZQ"/>
            </connections>
        </customObject>
        <menu id="zK2-oi-EfJ">
            <items>
                <menuItem title="Close" keyEquivalent="w" id="qQS-B4-nPy"/>
            </items>
            <point key="canvasLocation" x="560" y="-126"/>
        </menu>
    </objects>
    <resources>
        <image name="123.rectangle" catalog="system" width="24" height="18"/>
        <image name="dots.and.line.vertical.and.cursorarrow.rectangle" catalog="system" width="24" height="25"/>
        <image name="macwindow.on.rectangle" catalog="system" width="25" height="20"/>
        <image name="text.and.command.macwindow" catalog="system" width="24" height="18"/>
        <image name="uiwindow.split.2x1" catalog="system" width="24" height="18"/>
        <image name="waveform.path.ecg.rectangle" catalog="system" width="24" height="18"/>
    </resources>
</document>


================================================
FILE: Amethyst/Categories/NSRunningApplication+Manageable.swift
================================================
//
//  NSRunningApplication+Manageable.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/8/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import AppKit
import Foundation

enum Manageable {
    case manageable
    case unmanageable
    case undetermined
}

private let ignoredBundleIDs = Set([
    "com.apple.dashboard",
    "com.apple.loginwindow",
    "com.apple.notificationcenterui",
    "com.apple.wifi.WiFiAgent",
    "com.apple.Spotlight",
    "com.apple.systemuiserver",
    "com.apple.dock",
    "com.apple.AirPlayUIAgent",
    "com.apple.dock.extra",
    "com.apple.PowerChime",
    "com.apple.WebKit.Networking",
    "com.apple.WebKit.WebContent",
    "com.apple.WebKit.GPU",
    "com.apple.FollowUpUI",
    "com.apple.controlcenter",
    "com.apple.SoftwareUpdateNotificationManager",
    "com.apple.TextInputMenuAgent",
    "com.apple.TextInputSwitcher",
    "com.apple.WindowManager",
    "com.apple.accessibility.AXVisualSupportAgent",
    "com.apple.talagent",
    "com.apple.wallpaper.agent",
    "com.apple.CharacterPaletteIM",
    "com.apple.LocalAuthentication.UIAgent",
    "com.apple.security.Keychain-Circle-Notification",
    "com.apple.backgroundtaskmanagement.agent",
    "com.apple.CoreLocationAgent",
    "com.apple.OSDUIHelper",
    "com.apple.ViewBridgeAuxiliary"
])

protocol BundleIdentifiable {
    var bundleIdentifier: String? { get }
}

extension NSRunningApplication: BundleIdentifiable {}

extension NSRunningApplication {
    var isManageable: Manageable {
        if let bundleIdentifier = bundleIdentifier, ignoredBundleIDs.contains(bundleIdentifier) {
            return .unmanageable
        }

        guard isFinishedLaunching else {
            return .undetermined
        }

        guard case .regular = activationPolicy else {
            return .undetermined
        }

        return .manageable
    }
}


================================================
FILE: Amethyst/Categories/NSTableView+Amethyst.swift
================================================
//
//  NSTableView+Amethyst.swift
//  Amethyst
//
//  Created by James Zaghini on 15/5/18.
//  Copyright © 2018 Ian Ynda-Hummel. All rights reserved.
//

import Cocoa

extension NSTableView {
    static let noRowSelectedIndex = -1
}


================================================
FILE: Amethyst/Debug/AppsInfo.swift
================================================
//
//  AppsInfo.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 3/9/23.
//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.
//

import ArgumentParser
import Cocoa
import Silica

struct Apps: ParsableCommand {
    @Flag(help: "Include unmanaged applications.")
    var includeUnmanaged = false

    mutating func run() throws {
        let applications = NSWorkspace.shared.runningApplications
        for application in applications where includeUnmanaged || application.isManageable == .manageable {
            let app = SIApplication(runningApplication: application)
            print("""
            Title: \(app.title() ?? "<no title>")
            Bundle Identifier: \(application.bundleIdentifier ?? "<no id>")
            Activation Policy: \(application.activationPolicy)
            pid: \(app.pid())
            Manageable: \(application.isManageable)

            """)
        }
    }
}


================================================
FILE: Amethyst/Debug/DebugInfo.swift
================================================
//
//  DebugInfo.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 2/24/20.
//  Copyright © 2020 Ian Ynda-Hummel. All rights reserved.
//

import ArgumentParser
import Cocoa

struct Debug: ParsableCommand {
    static var configuration: CommandConfiguration = CommandConfiguration(
        abstract: "Generate diagnostic reports on system state.",
        subcommands: [Apps.self, Windows.self],
        defaultSubcommand: Windows.self
    )
}

struct DebugInfo {
    static func description(arguments: [String]) -> String {
        var infos = [
            "Version: \(version())",
            "OS version: \(ProcessInfo.processInfo.operatingSystemVersionString)",
            "Screens:\n\(screens())",
            "Configuration:\n\(config())"
        ]

        if arguments.contains("--include-apps") {
            infos.append("Manageable applications:\n\(applications())")
        }

        return infos.joined(separator: "\n\n")
    }

    static func version() -> String {
        let version = Bundle.main.infoDictionary?["CFBundleVersion"] as! String
        let shortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
        return "\(shortVersion) (\(version))"
    }

    static func isProcessTrusted() -> Bool {
        let options = [
            kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: false
        ]

        return AXIsProcessTrustedWithOptions(options as CFDictionary)
    }

    static func screens() -> String {
        return NSScreen.screens.map { "\t\($0.frame) [\($0.frameIncludingDockAndMenu())]" }.joined(separator: "\n")
    }

    static func applications() -> String {
        return NSWorkspace.shared.runningApplications
            .filter { $0.isManageable == .manageable }
            .map { "\t\($0.localizedName ?? "<unknown name>") (\($0.bundleIdentifier ?? "<unknown bundle id>"))" }
            .joined(separator: "\n")
    }

    static func config() -> String {
        return UserDefaults.standard.dictionaryRepresentation()
            .filter { ConfigurationKey(rawValue: $0.key) != nil }
            .map { "\($0): \($1)" }.joined(separator: "\n")
    }
}


================================================
FILE: Amethyst/Debug/ScreensInfo.swift
================================================
//
//  ScreensInfo.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 3/9/23.
//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.
//

import ArgumentParser
import Cocoa
import Silica

extension AMScreen {
    func debugDescription() -> String {
        return """
        \tScreenID: \(screenID()!)
        \tScreen Frame: \(frame())
        """
    }
}

struct Screens: ParsableCommand {
    mutating func run() throws {
    }
}


================================================
FILE: Amethyst/Debug/WindowsInfo.swift
================================================
//
//  WindowsInfo.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 3/5/23.
//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.
//

import ArgumentParser
import Cocoa
import Foundation
import Silica

extension AXWindow {
    func debugInfo(redactTitles: Bool) -> String {
        let screenDescription = screen().map { AMScreen(screen: $0).debugDescription() }

        return """
        \tTitle: \(redactTitles ? "<redacted>" : title() ?? "<no title>")
        \tFrame: \(frame())
        \tid: \(windowID())
        \(screenDescription ?? "Screen: unknown")
        \tisActive: \(isActive())
        \tisOnScreen: \(isOnScreen())
        \tisFocused: \(isFocused())
        \tshouldBeManaged: \(shouldBeManaged())
        \tshouldFloat: \(shouldFloat())
        """
    }
}

struct Windows: ParsableCommand {
    @Flag(help: "Include windows of unmanaged applications.")
    var includeUnmanaged = false

    @Flag(help: "Redact window titles.")
    var redactWindowTitles = false

    mutating func run() throws {
        let applications = NSWorkspace.shared.runningApplications
        for application in applications where includeUnmanaged || application.isManageable == .manageable {
            let app = SIApplication(runningApplication: application)
            print("\(app.title() ?? "<no title>") (pid \(app.pid()))")
            for window in app.windows() {
                let axWindow = AXWindow(element: window)!
                print(axWindow.debugInfo(redactTitles: redactWindowTitles))
                print("")
            }
        }
    }
}


================================================
FILE: Amethyst/Events/HotKeyManager.swift
================================================
//
//  HotKeyManager.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/15/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import Carbon
import Foundation
import Silica

// Type for defining key code.
typealias AMKeyCode = Int

// Type for defining modifier flags.
typealias AMModifierFlags = NSEvent.ModifierFlags

// Specific key code defined to be invalid.
// Can be used to identify if a returned key code is valid or not.
private let AMKeyCodeInvalid: AMKeyCode = 0xFF

typealias HotKeyHandler = () -> Void

class HotKeyManager<Application: ApplicationType>: NSObject {
    private let userConfiguration: UserConfiguration

    private(set) lazy var stringToKeyCodes: [String: [AMKeyCode]] = {
        return self.constructKeyCodeMap()
    }()

    init(userConfiguration: UserConfiguration) {
        self.userConfiguration = userConfiguration
        super.init()
        _ = constructKeyCodeMap()
    }

    private static func keyCodeForNumber(_ number: NSNumber) -> AMKeyCode {
        let string = "\(number)"

        guard !string.isEmpty else {
            return AMKeyCodeInvalid
        }

        switch string.last! {
        case "1":
            return kVK_ANSI_1
        case "2":
            return kVK_ANSI_2
        case "3":
            return kVK_ANSI_3
        case "4":
            return kVK_ANSI_4
        case "5":
            return kVK_ANSI_5
        case "6":
            return kVK_ANSI_6
        case "7":
            return kVK_ANSI_7
        case "8":
            return kVK_ANSI_8
        case "9":
            return kVK_ANSI_9
        case "0":
            return kVK_ANSI_0
        default:
            return AMKeyCodeInvalid
        }
    }

    func setUpWithWindowManager(_ windowManager: WindowManager<Application>, configuration: UserConfiguration, appDelegate: AppDelegate) {
        constructCommandWithCommandKey(CommandKey.cycleLayoutForward.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.cycleLayoutForward()
        }

        constructCommandWithCommandKey(CommandKey.cycleLayoutBackward.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.cycleLayoutBackward()
        }

        constructCommandWithCommandKey(CommandKey.shrinkMain.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let panedLayout = layout as? PanedLayout {
                    panedLayout.shrinkMainPane()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.expandMain.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let panedLayout = layout as? PanedLayout {
                    panedLayout.expandMainPane()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.increaseMain.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let panedLayout = layout as? PanedLayout {
                    panedLayout.increaseMainPaneCount()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.decreaseMain.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let panedLayout = layout as? PanedLayout {
                    panedLayout.decreaseMainPaneCount()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.command1.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let customLayout = layout as? CustomLayout {
                    customLayout.command1()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.command2.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let customLayout = layout as? CustomLayout {
                    customLayout.command2()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.command3.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let customLayout = layout as? CustomLayout {
                    customLayout.command3()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.command4.rawValue) {
            let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
            screenManager?.updateCurrentLayout { layout in
                if let customLayout = layout as? CustomLayout {
                    customLayout.command4()
                }
            }
        }

        constructCommandWithCommandKey(CommandKey.focusCCW.rawValue) {
            windowManager.focusTransitionCoordinator.moveFocusCounterClockwise()
        }

        constructCommandWithCommandKey(CommandKey.focusCW.rawValue) {
            windowManager.focusTransitionCoordinator.moveFocusClockwise()
        }

        constructCommandWithCommandKey(CommandKey.focusMain.rawValue) {
            windowManager.focusTransitionCoordinator.moveFocusToMain()
        }

        constructCommandWithCommandKey(CommandKey.focusScreenCCW.rawValue) {
            windowManager.focusTransitionCoordinator.moveFocusScreenCounterClockwise()
        }

        constructCommandWithCommandKey(CommandKey.focusScreenCW.rawValue) {
            windowManager.focusTransitionCoordinator.moveFocusScreenClockwise()
        }

        constructCommandWithCommandKey(CommandKey.swapScreenCCW.rawValue) {
            windowManager.windowTransitionCoordinator.swapFocusedWindowScreenCounterClockwise()
        }

        constructCommandWithCommandKey(CommandKey.swapScreenCW.rawValue) {
            windowManager.windowTransitionCoordinator.swapFocusedWindowScreenClockwise()
        }

        constructCommandWithCommandKey(CommandKey.swapCCW.rawValue) {
            windowManager.windowTransitionCoordinator.swapFocusedWindowCounterClockwise()
        }

        constructCommandWithCommandKey(CommandKey.swapCW.rawValue) {
            windowManager.windowTransitionCoordinator.swapFocusedWindowClockwise()
        }

        constructCommandWithCommandKey(CommandKey.swapMain.rawValue) {
            windowManager.windowTransitionCoordinator.swapFocusedWindowToMain()
        }

        constructCommandWithCommandKey(CommandKey.displayCurrentLayout.rawValue) {
            DispatchQueue.main.async {
                windowManager.displayCurrentLayout()
            }
        }

        (1...5).forEach { screenNumber in
            let focusCommandKey = "\(CommandKey.focusScreenPrefix.rawValue)-\(screenNumber)"
            let throwCommandKey = "\(CommandKey.throwScreenPrefix.rawValue)-\(screenNumber)"

            self.constructCommandWithCommandKey(focusCommandKey) {
                windowManager.focusTransitionCoordinator.focusScreen(at: screenNumber - 1)
            }

            self.constructCommandWithCommandKey(throwCommandKey) {
                windowManager.windowTransitionCoordinator.throwToScreenAtIndex(screenNumber - 1)
            }
        }

        (1...16).forEach { spaceNumber in
            let commandKey = "\(CommandKey.throwSpacePrefix.rawValue)-\(spaceNumber)"

            self.constructCommandWithCommandKey(commandKey) {
                windowManager.windowTransitionCoordinator.pushFocusedWindowToSpace(spaceNumber - 1)
            }
        }

        constructCommandWithCommandKey(CommandKey.throwSpaceLeft.rawValue) {
            windowManager.windowTransitionCoordinator.pushFocusedWindowToSpaceLeft()
        }

        constructCommandWithCommandKey(CommandKey.throwSpaceRight.rawValue) {
            windowManager.windowTransitionCoordinator.pushFocusedWindowToSpaceRight()
        }

        constructCommandWithCommandKey(CommandKey.toggleFloat.rawValue) {
            windowManager.toggleFloatForFocusedWindow()
        }

        constructCommandWithCommandKey(CommandKey.toggleTiling.rawValue) {
            self.userConfiguration.tilingEnabled = !self.userConfiguration.tilingEnabled
            windowManager.markAllScreensForReflow()
        }

        constructCommandWithCommandKey(CommandKey.enableTiling.rawValue) {
            guard !self.userConfiguration.tilingEnabled else { return }
            self.userConfiguration.tilingEnabled = true
            windowManager.markAllScreensForReflow()
        }

        constructCommandWithCommandKey(CommandKey.disableTiling.rawValue) {
            guard self.userConfiguration.tilingEnabled else { return }
            self.userConfiguration.tilingEnabled = false
            windowManager.markAllScreensForReflow()
        }

        constructCommandWithCommandKey(CommandKey.reevaluateWindows.rawValue) {
            windowManager.reevaluateWindows()
        }

        constructCommandWithCommandKey(CommandKey.toggleFocusFollowsMouse.rawValue) {
            self.userConfiguration.toggleFocusFollowsMouse()
        }

        constructCommandWithCommandKey(CommandKey.relaunchAmethyst.rawValue) { [weak appDelegate] in
            appDelegate?.relaunch(self)
        }

        constructCommandWithCommandKey(CommandKey.increaseWindowMaxCount.rawValue) {
            self.userConfiguration.increaseWindowMaxCount()
            windowManager.markAllScreensForReflow()
            DispatchQueue.main.async {
                windowManager.displayWindowCountHUD()
            }
        }

        constructCommandWithCommandKey(CommandKey.decreaseWindowMaxCount.rawValue) {
            self.userConfiguration.decreaseWindowMaxCount()
            windowManager.markAllScreensForReflow()
            DispatchQueue.main.async {
                windowManager.displayWindowCountHUD()
            }
        }

        LayoutType<Application.Window>.availableLayoutStrings().forEach { (layoutKey, _) in
            self.constructCommandWithCommandKey(UserConfiguration.constructLayoutKeyString(layoutKey)) {
                let screenManager: ScreenManager<WindowManager<Application>>? = windowManager.focusedScreenManager()
                screenManager?.selectLayout(layoutKey)
            }
        }
    }

    private func constructKeyCodeMap() -> [String: [AMKeyCode]] {
        var stringToKeyCodes: [String: [AMKeyCode]] = [:]

        // Generate unicode character keymapping from keyboard layout data.  We go
        // through all keycodes and create a map of string representations to a list
        // of key codes. It has to map to a list because a string representation
        // canmap to multiple codes (e.g., 1 and numpad 1 both have string
        // representation "1").
        var currentKeyboard = TISCopyCurrentKeyboardInputSource().takeRetainedValue()
        var rawLayoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData)

        if rawLayoutData == nil {
            currentKeyboard = TISCopyCurrentASCIICapableKeyboardLayoutInputSource().takeUnretainedValue()
            rawLayoutData = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData)
        }

        // Get the layout
        let layoutData = unsafeBitCast(rawLayoutData, to: CFData.self)
        let layout: UnsafePointer<UCKeyboardLayout> = unsafeBitCast(CFDataGetBytePtr(layoutData), to: UnsafePointer<UCKeyboardLayout>.self)

        var keysDown: UInt32 = 0
        var chars: [UniChar] = [0, 0, 0, 0]
        var realLength: Int = 0

        for keyCode in (0..<AMKeyCodeInvalid) {
            switch keyCode {
            case
            kVK_ANSI_Keypad0,
            kVK_ANSI_Keypad1,
            kVK_ANSI_Keypad2,
            kVK_ANSI_Keypad3,
            kVK_ANSI_Keypad4,
            kVK_ANSI_Keypad5,
            kVK_ANSI_Keypad6,
            kVK_ANSI_Keypad7,
            kVK_ANSI_Keypad8,
            kVK_ANSI_Keypad9:
                continue
            default:
                break
            }

            UCKeyTranslate(layout,
                           UInt16(keyCode),
                           UInt16(kUCKeyActionDisplay),
                           0,
                           UInt32(LMGetKbdType()),
                           UInt32(kUCKeyTranslateNoDeadKeysBit),
                           &keysDown,
                           chars.count,
                           &realLength,
                           &chars)

            let string = CFStringCreateWithCharacters(kCFAllocatorDefault, chars, realLength) as String

            if let keyCodes = stringToKeyCodes[string] {
                var mutableKeyCodes = keyCodes
                mutableKeyCodes.append(keyCode)
                stringToKeyCodes[string] = mutableKeyCodes
            } else {
                stringToKeyCodes[string] = [keyCode]
            }
        }

        // Add codes for non-printable characters. They are not printable so they
        // are not generated from the keyboard layout data.
        stringToKeyCodes["space"] = [kVK_Space]
        stringToKeyCodes["enter"] = [kVK_Return]
        stringToKeyCodes["up"] = [kVK_UpArrow]
        stringToKeyCodes["right"] = [kVK_RightArrow]
        stringToKeyCodes["down"] = [kVK_DownArrow]
        stringToKeyCodes["left"] = [kVK_LeftArrow]
        stringToKeyCodes["delete"] = [kVK_Delete]

        return stringToKeyCodes
    }

    private func constructCommandWithCommandKey(_ commandKey: String, handler: @escaping HotKeyHandler) {
        userConfiguration.constructCommand(for: self, commandKey: commandKey, handler: handler)
    }

    private func carbonModifiersFromModifiers(_ modifiers: UInt) -> UInt32 {
        var carbonModifiers: UInt32 = 0

        if (modifiers & UInt(NSEvent.ModifierFlags.shift.rawValue)) > 0 {
            carbonModifiers = carbonModifiers | UInt32(shiftKey)
        }

        if (modifiers & UInt(NSEvent.ModifierFlags.command.rawValue)) > 0 {
            carbonModifiers = carbonModifiers | UInt32(cmdKey)
        }

        if (modifiers & UInt(NSEvent.ModifierFlags.option.rawValue)) > 0 {
            carbonModifiers = carbonModifiers | UInt32(optionKey)
        }

        if (modifiers & UInt(NSEvent.ModifierFlags.control.rawValue)) > 0 {
            carbonModifiers = carbonModifiers | UInt32(controlKey)
        }

        return carbonModifiers
    }

    static func hotKeyNameToDefaultsKey() -> [[String]] {
        var hotKeyNameToDefaultsKey: [[String]] = []

        hotKeyNameToDefaultsKey.append(["Cycle layout forward", CommandKey.cycleLayoutForward.rawValue])
        hotKeyNameToDefaultsKey.append(["Cycle layout backwards", CommandKey.cycleLayoutBackward.rawValue])
        hotKeyNameToDefaultsKey.append(["Shrink main pane", CommandKey.shrinkMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Expand main pane", CommandKey.expandMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Increase main pane count", CommandKey.increaseMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Decrease main pane count", CommandKey.decreaseMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Increase window max count", CommandKey.increaseWindowMaxCount.rawValue])
        hotKeyNameToDefaultsKey.append(["Decrease window max count", CommandKey.decreaseWindowMaxCount.rawValue])
        hotKeyNameToDefaultsKey.append(["Custom layout command 1", CommandKey.command1.rawValue])
        hotKeyNameToDefaultsKey.append(["Custom layout command 2", CommandKey.command2.rawValue])
        hotKeyNameToDefaultsKey.append(["Custom layout command 3", CommandKey.command3.rawValue])
        hotKeyNameToDefaultsKey.append(["Custom layout command 4", CommandKey.command4.rawValue])
        hotKeyNameToDefaultsKey.append(["Move focus counter clockwise", CommandKey.focusCCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Move focus clockwise", CommandKey.focusCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Move focus to main window", CommandKey.focusMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Move focus to counter clockwise screen", CommandKey.focusScreenCCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Move focus to clockwise screen", CommandKey.focusScreenCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Swap focused window to counter clockwise screen", CommandKey.swapScreenCCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Swap focused window to clockwise screen", CommandKey.swapScreenCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Swap focused window counter clockwise", CommandKey.swapCCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Swap focused window clockwise", CommandKey.swapCW.rawValue])
        hotKeyNameToDefaultsKey.append(["Swap focused window with main window", CommandKey.swapMain.rawValue])
        hotKeyNameToDefaultsKey.append(["Force windows to be reevaluated", CommandKey.reevaluateWindows.rawValue])
        hotKeyNameToDefaultsKey.append(["Throw focused window to space left", CommandKey.throwSpaceLeft.rawValue])
        hotKeyNameToDefaultsKey.append(["Throw focused window to space right", CommandKey.throwSpaceRight.rawValue])

        (1...16).forEach { spaceNumber in
            let name = "Throw focused window to space \(spaceNumber)"

            hotKeyNameToDefaultsKey.append([name, "\(CommandKey.throwSpacePrefix.rawValue)-\(spaceNumber)"])
        }

        (1...5).forEach { screenNumber in
            let focusCommandName = "Focus screen \(screenNumber)"
            let throwCommandName = "Throw focused window to screen \(screenNumber)"
            let focusCommandKey = "\(CommandKey.focusScreenPrefix.rawValue)-\(screenNumber)"
            let throwCommandKey = "\(CommandKey.throwScreenPrefix.rawValue)-\(screenNumber)"

            hotKeyNameToDefaultsKey.append([focusCommandName, focusCommandKey])
            hotKeyNameToDefaultsKey.append([throwCommandName, throwCommandKey])
        }

        hotKeyNameToDefaultsKey.append(["Toggle float for focused window", CommandKey.toggleFloat.rawValue])
        hotKeyNameToDefaultsKey.append(["Display current layout", CommandKey.displayCurrentLayout.rawValue])
        hotKeyNameToDefaultsKey.append(["Toggle focus follows mouse", CommandKey.toggleFocusFollowsMouse.rawValue])
        hotKeyNameToDefaultsKey.append(["Toggle global tiling", CommandKey.toggleTiling.rawValue])
        hotKeyNameToDefaultsKey.append(["Enable global tiling", CommandKey.enableTiling.rawValue])
        hotKeyNameToDefaultsKey.append(["Disable global tiling", CommandKey.disableTiling.rawValue])

        for (layoutKey, layoutName) in LayoutType<Application.Window>.availableLayoutStrings() {
            let commandName = "Select \(layoutName) layout"
            let commandKey = "select-\(layoutKey)-layout"
            hotKeyNameToDefaultsKey.append([commandName, commandKey])
        }

        hotKeyNameToDefaultsKey.append(["Relaunch Amethyst", CommandKey.relaunchAmethyst.rawValue])

        return hotKeyNameToDefaultsKey
    }
}


================================================
FILE: Amethyst/Images.xcassets/123.rectangle.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "123.rectangle.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icon_16x16.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "16x16"
    },
    {
      "filename" : "icon_16x16@2x.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "16x16"
    },
    {
      "filename" : "icon_32x32.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "32x32"
    },
    {
      "filename" : "icon_32x32@2x.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "32x32"
    },
    {
      "filename" : "icon_128x128.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "128x128"
    },
    {
      "filename" : "icon_128x128@2x.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "128x128"
    },
    {
      "filename" : "icon_256x256.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "256x256"
    },
    {
      "filename" : "icon_256x256@2x.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "256x256"
    },
    {
      "filename" : "icon_512x512.png",
      "idiom" : "mac",
      "scale" : "1x",
      "size" : "512x512"
    },
    {
      "filename" : "icon_512x512@2x.png",
      "idiom" : "mac",
      "scale" : "2x",
      "size" : "512x512"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/amethyst.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icon_32x32.png",
      "idiom" : "mac",
      "scale" : "1x"
    },
    {
      "filename" : "icon_32x32@2x.png",
      "idiom" : "mac",
      "scale" : "2x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/dots.and.line.vertical.and.cursorarrow.rectangle.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "dots.and.line.vertical.and.cursorarrow.rectangle.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/icon-statusitem-disabled.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icon-statusitem-disabled.png",
      "idiom" : "mac",
      "scale" : "1x"
    },
    {
      "filename" : "icon-statusitem-disabled@2x.png",
      "idiom" : "mac",
      "scale" : "2x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/icon-statusitem.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "icon-statusitem.png",
      "idiom" : "mac",
      "scale" : "1x"
    },
    {
      "filename" : "icon-statusitem@2x.png",
      "idiom" : "mac",
      "scale" : "2x"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/macwindow.on.rectangle.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "macwindow.on.rectangle.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/text.and.command.macwindow.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "text.and.command.macwindow.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/uiwindow.split.2x1.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "uiwindow.split.2x1.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Images.xcassets/waveform.path.ecg.rectangle.imageset/Contents.json
================================================
{
  "images" : [
    {
      "filename" : "waveform.path.ecg.rectangle.svg",
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Amethyst/Layout/Layout.swift
================================================
//
//  Layout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/3/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Foundation
import Silica

/**
 A base class for specific layout algorithms defining size and position of windows.
 
 - Requires:
 Specific layouts must subclass and override the following properties and methods:
 - `layoutName`
 - `layoutKey`
 
 Subclasses can optionally override `layoutDescription` to provide debugging information for the layout state.
 
 - Note:
 Usage of a layout object requires specifying a `WindowType` parameter.
 */
class Layout<Window: WindowType>: Codable {
    typealias Screen = Window.Screen

    private enum CodingKeys: String, CodingKey {
        case key
    }

    /// The display name of the layout.
    class var layoutName: String { fatalError("Must be implemented by subclass") }

    /// The configuration key of the layout.
    class var layoutKey: String { fatalError("Must be implemented by subclass") }

    /// The display name of the layout.
    var layoutName: String { return type(of: self).layoutName }

    /// The configuration key of the layout.
    var layoutKey: String { return type(of: self).layoutKey }

    /// The debug description of the layout.
    var layoutDescription: String { return "" }

    required init() {}

    required init(from decoder: Decoder) throws {}

    /// Base encoder for layouts; basically a noop.
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(layoutKey, forKey: .key)
    }

    /**
     Takes a list of windows and a screen and returns the assignments that would be performed.
     
     - Parameters:
         - windows: The windows to apply the layout algorithm to.
         - screen: The screen on which those windows should reside.
     
     - Returns:
     The assignments that would be performed given those windows on that screen.
     */
    func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        fatalError("Must be implemented by subclass")
    }
}

/// Errors occurring when decoding a layout
enum LayoutDecodingError: Error {
    /**
     Something about the layout was structurally unsound.
     
     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.
     */
    case invalidLayout
}

// MARK: Window Querying

extension Layout {
    /**
     Determines what window the layout would put at a given point.
     
     - Parameters:
         - point: The point to test for location.
         - windows: The windows to apply the layout algorithm to.
         - screen: The screen on which those windows should reside.
     
     - Returns:
     The window that the layout would intend to put at `point`.
     
     - 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.
     */
    func windowAtPoint(_ point: CGPoint, of windowSet: WindowSet<Window>, on screen: Screen) -> LayoutWindow<Window>? {
        return frameAssignments(windowSet, on: screen)?
            .map { $0.frameAssignment }
            .first { $0.frame.contains(point) }?
            .window
    }

    /**
     Determines what frame the layout would apply to a given window.
     
     - Parameters:
         - window: The window to test for frame.
         - windows: The windows to apply the layout algorithm to.
         - screen: The screen on which those windows should reside.
     
     - Returns:
     The `FrameAssignment` object defining the size and location that the layout would assign to `window`.
     
     - 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.
     */
    func assignedFrame(_ window: Window, of windowSet: WindowSet<Window>, on screen: Screen) -> FrameAssignment<Window>? {
        guard let assignments = frameAssignments(windowSet, on: screen) else {
            return nil
        }

        return assignments.map { $0.frameAssignment }.first { $0.window.id == window.id() }
    }
}

/**
 A particular kind of layout that organizes windows into a main pane and any number of sub-panes.
 
 - Note:
 The definition is intentionally somewhat layout. This is more intended to demonstrate the expected interface for a fairly common paradigm in Amethyst layouts.
 */
protocol PanedLayout {
    /**
     The ratio of the size of the main pane to the size of the sub-panes.
     
     - Requires:
     The value must be between 0 and 1, inclusive.
     */
    var mainPaneRatio: CGFloat { get }

    /// The number of windows that make up the main pane.
    var mainPaneCount: Int { get }

    /**
     Takes a direct recommendation for a change in ratio.
     
     - Parameters:
        - rawRatio: The ratio recommended by the caller.
     
     - Requires:
     `rawRatio` must be a valid ratio.
     
     - Note: This method should generally be reserved for internal use by the layout.
     */
    func recommendMainPaneRawRatio(rawRatio: CGFloat)

    /// Reduces the visual footprint of the main pane relative to the sub-panes.
    func shrinkMainPane()

    /// Increases the visual footprint of the main pane relative to the sub-panes.
    func expandMainPane()

    /// Increases the number of windows that make up the main pane.
    func increaseMainPaneCount()

    /// Decreases the number of windows that make up the main pane.
    func decreaseMainPaneCount()
}

extension PanedLayout {
    /// The default debug layout description for paned layouts. It describes the ratio and number of main pane windows.
    var layoutDescription: String {
        return "(\(mainPaneRatio), \(mainPaneCount))"
    }

    /**
     Takes a recommendation for a change in ratio, but can modify the ratio to adjust for internal state.
     
     - Parameters:
        - ratio: The ratio recommended by the caller.
     */
    func recommendMainPaneRatio(_ ratio: CGFloat) {
        guard 0 <= ratio && ratio <= 1 else {
            log.warning("tried to setMainPaneRatio out of range [0-1]:  \(ratio)")
            return recommendMainPaneRawRatio(rawRatio: max(min(ratio, 1), 0))
        }
        recommendMainPaneRawRatio(rawRatio: ratio)
    }

    /// The default behavior of main pane expansion that simply recommends an increase in ratio by the configured resize step.
    func expandMainPane() {
        recommendMainPaneRatio(mainPaneRatio + UserConfiguration.shared.windowResizeStep())
    }

    /// The default behavior of main pane shrinking that simply recommends a decrease in ratio by the configured resize step.
    func shrinkMainPane() {
        recommendMainPaneRatio(mainPaneRatio - UserConfiguration.shared.windowResizeStep())
    }
}

/**
 A base class for specific layout algorithms that maintain internal state for defining size and position of windows.
 
 - Requires:
 Specific layouts must subclass and override the following properties and methods:
 - `updateWithChange(_ windowChange: WindowChange<Window>)`
 - `nextWindowIDCounterClockwise() -> CGWindowID?`
 - `nextWindowIDClockwise() -> CGWindowID?`
 
 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.
 */
class StatefulLayout<Window: WindowType>: Layout<Window> {
    /**
     Updates internal state of the layout based on a window change.
     
     - Parameters:
        - windowChange: A `WindowChange`.
     */
    func updateWithChange(_ windowChange: Change<Window>) {
        fatalError("Must be implemented by subclass")
    }

    /**
     Determines the window that is before the current window.
     
     - Returns:
     The ID of the window before the current window.
     */
    func nextWindowIDCounterClockwise() -> Window.WindowID? {
        fatalError("Must be implemented by subclass")
    }

    /**
     Determines the window that is after the current window.
     
     - Returns:
     The ID of the window after the current window.
     */
    func nextWindowIDClockwise() -> Window.WindowID? {
        fatalError("Must be implemented by subclass")
    }
}


================================================
FILE: Amethyst/Layout/Layouts/BinarySpacePartitioningLayout.swift
================================================
//
//  BinarySpacePartitioningLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/29/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class TreeNode<Window: WindowType>: Codable {
    typealias WindowID = Window.WindowID

    private enum CodingKeys: String, CodingKey {
        case left
        case right
        case windowID
    }

    weak var parent: TreeNode?
    var left: TreeNode?
    var right: TreeNode?
    var windowID: WindowID?

    init() {}

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.left = try values.decodeIfPresent(TreeNode.self, forKey: .left)
        self.right = try values.decodeIfPresent(TreeNode.self, forKey: .right)
        self.windowID = try values.decodeIfPresent(WindowID.self, forKey: .windowID)

        self.left?.parent = self
        self.right?.parent = self

        guard valid else {
            throw LayoutDecodingError.invalidLayout
        }
    }

    var valid: Bool {
        return (left != nil && right != nil && windowID == nil) || (left == nil && right == nil && windowID != nil)
    }

    func findWindowID(_ windowID: WindowID) -> TreeNode? {
        guard self.windowID == windowID else {
            return left?.findWindowID(windowID) ?? right?.findWindowID(windowID)
        }

        return self
    }

    func orderedWindowIDs() -> [WindowID] {
        guard let windowID = windowID else {
            let leftWindowIDs = left?.orderedWindowIDs() ?? []
            let rightWindowIDs = right?.orderedWindowIDs() ?? []
            return leftWindowIDs + rightWindowIDs
        }

        return [windowID]
    }

    func insertWindowIDAtEnd(_ windowID: WindowID) {
        guard left == nil && right == nil else {
            right?.insertWindowIDAtEnd(windowID)
            return
        }

        insertWindowID(windowID)
    }

    func insertWindowID(_ windowID: WindowID, atPoint insertionPoint: WindowID) {
        guard self.windowID == insertionPoint else {
            left?.insertWindowID(windowID, atPoint: insertionPoint)
            right?.insertWindowID(windowID, atPoint: insertionPoint)
            return
        }

        insertWindowID(windowID)
    }

    func removeWindowID(_ windowID: WindowID) {
        guard let node = findWindowID(windowID) else {
            log.error("Trying to remove window not in tree")
            return
        }

        guard let parent = node.parent else {
            return
        }

        guard let grandparent = parent.parent else {
            if node == parent.left {
                parent.windowID = parent.right?.windowID
            } else {
                parent.windowID = parent.left?.windowID
            }
            parent.left = nil
            parent.right = nil
            return
        }

        if parent == grandparent.left {
            if node == parent.left {
                grandparent.left = parent.right
            } else {
                grandparent.left = parent.left
            }
            grandparent.left?.parent = grandparent
        } else {
            if node == parent.left {
                grandparent.right = parent.right
            } else {
                grandparent.right = parent.left
            }
            grandparent.right?.parent = grandparent
        }
    }

    func insertWindowID(_ windowID: WindowID) {
        guard parent != nil || self.windowID != nil else {
            self.windowID = windowID
            return
        }

        if let parent = parent {
            let newParent = TreeNode()
            let newNode = TreeNode()

            newNode.parent = newParent
            newNode.windowID = windowID

            newParent.left = self
            newParent.right = newNode
            newParent.parent = parent

            if self == parent.left {
                parent.left = newParent
            } else {
                parent.right = newParent
            }

            self.parent = newParent
        } else {
            let newSelf = TreeNode()
            let newNode = TreeNode()

            newSelf.windowID = self.windowID
            self.windowID = nil

            newNode.windowID = windowID

            left = newSelf
            left?.parent = self
            right = newNode
            right?.parent = self
        }
    }
}

extension TreeNode: Equatable {
    static func == (lhs: TreeNode, rhs: TreeNode) -> Bool {
        return lhs.windowID == rhs.windowID && lhs.left == rhs.left && lhs.right == rhs.right
    }
}

class BinarySpacePartitioningLayout<Window: WindowType>: StatefulLayout<Window> {
    typealias WindowID = Window.WindowID

    private typealias TraversalNode = (node: TreeNode<Window>, frame: CGRect)

    private enum CodingKeys: String, CodingKey {
        case rootNode
    }

    override static var layoutName: String { return "Binary Space Partitioning" }
    override static var layoutKey: String { return "bsp" }

    override var layoutDescription: String { return "\(lastKnownFocusedWindowID.debugDescription)" }

    private var rootNode = TreeNode<Window>()
    private var lastKnownFocusedWindowID: WindowID?

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.rootNode = try container.decode(TreeNode<Window>.self, forKey: .rootNode)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(rootNode, forKey: .rootNode)
    }

    private func constructInitialTreeWithWindows(_ windows: [LayoutWindow<Window>]) {
        for window in windows {
            guard rootNode.findWindowID(window.id) == nil else {
                continue
            }

            rootNode.insertWindowIDAtEnd(window.id)

            if window.isFocused {
                lastKnownFocusedWindowID = window.id
            }
        }
    }

    override func updateWithChange(_ windowChange: Change<Window>) {
        switch windowChange {
        case let .add(window):
            guard rootNode.findWindowID(window.id()) == nil else {
                log.warning("Trying to add a window already in the tree")
                return
            }

            if let insertionPoint = lastKnownFocusedWindowID, window.id() != insertionPoint, rootNode.findWindowID(insertionPoint) != nil {
                log.info("insert \(window) - \(window.id()) at point: \(insertionPoint)")
                rootNode.insertWindowID(window.id(), atPoint: insertionPoint)
            } else {
                log.info("insert \(window) - \(window.id()) at end")
                rootNode.insertWindowIDAtEnd(window.id())
            }

            if window.isFocused() {
                lastKnownFocusedWindowID = window.id()
            }
        case let .remove(window):
            log.info("remove: \(window) - \(window.id())")
            rootNode.removeWindowID(window.id())
        case let .focusChanged(window):
            lastKnownFocusedWindowID = window.id()
        case let .windowSwap(window, otherWindow):
            let windowID = window.id()
            let otherWindowID = otherWindow.id()

            guard let windowNode = rootNode.findWindowID(windowID), let otherWindowNode = rootNode.findWindowID(otherWindowID) else {
                log.error("Tried to perform an unbalanced window swap: \(windowID) <-> \(otherWindowID)")
                return
            }

            windowNode.windowID = otherWindowID
            otherWindowNode.windowID = windowID
        case let .tabChange(window, previousWindow):
            if rootNode.findWindowID(window.id()) != nil {
                log.warning("Trying to swap a tab in that is already in the tree: \(window)")
                rootNode.removeWindowID(window.id())
            }

            guard let previousWindowNode = rootNode.findWindowID(previousWindow.id()) else {
                log.error("Trying to change tab from a window that is not in the tree: \(previousWindow)")
                return
            }

            if let windowNode = rootNode.findWindowID(window.id()) {
                log.warning("Trying to swap a tab in from another node")
            }

            previousWindowNode.windowID = window.id()
        case .applicationDeactivate, .applicationActivate, .spaceChange, .layoutChange, .none, .unknown:
            break
        }
    }

    override func nextWindowIDCounterClockwise() -> WindowID? {
        guard let focusedWindow = Window.currentlyFocused() else {
            return nil
        }

        let orderedIDs = rootNode.orderedWindowIDs()

        guard let focusedWindowIndex = orderedIDs.firstIndex(of: focusedWindow.id()) else {
            return nil
        }

        let nextWindowIndex = (focusedWindowIndex == 0 ? orderedIDs.count - 1 : focusedWindowIndex - 1)

        return orderedIDs[nextWindowIndex]
    }

    override func nextWindowIDClockwise() -> WindowID? {
        guard let focusedWindow = Window.currentlyFocused() else {
            return nil
        }

        let orderedIDs = rootNode.orderedWindowIDs()

        guard let focusedWindowIndex = orderedIDs.firstIndex(of: focusedWindow.id()) else {
            return nil
        }

        let nextWindowIndex = (focusedWindowIndex == orderedIDs.count - 1 ? 0 : focusedWindowIndex + 1)

        return orderedIDs[nextWindowIndex]
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        if rootNode.left == nil && rootNode.right == nil {
            constructInitialTreeWithWindows(windows)
        }

        let windowIDMap: [WindowID: LayoutWindow<Window>] = windows.reduce([:]) { (windowMap, window) -> [WindowID: LayoutWindow<Window>] in
            var mutableWindowMap = windowMap
            mutableWindowMap[window.id] = window
            return mutableWindowMap
        }

        let baseFrame = screen.adjustedFrame()
        var ret: [FrameAssignmentOperation<Window>] = []
        var traversalNodes: [TraversalNode] = [(node: rootNode, frame: baseFrame)]

        while !traversalNodes.isEmpty {
            let traversalNode = traversalNodes[0]

            traversalNodes = [TraversalNode](traversalNodes.dropFirst(1))

            if let windowID = traversalNode.node.windowID {
                guard let window = windowIDMap[windowID] else {
                    log.warning("Could not find window for ID: \(windowID)")
                    continue
                }

                let resizeRules = ResizeRules(isMain: true, unconstrainedDimension: .horizontal, scaleFactor: 1)
                let frameAssignment = FrameAssignment<Window>(
                    frame: traversalNode.frame,
                    window: window,
                    screenFrame: baseFrame,
                    resizeRules: resizeRules
                )
                ret.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))
            } else {
                guard let left = traversalNode.node.left, let right = traversalNode.node.right else {
                    log.error("Encountered an invalid node")
                    continue
                }

                let frame = traversalNode.frame

                if frame.width > frame.height {
                    let leftFrame = CGRect(
                        x: frame.origin.x,
                        y: frame.origin.y,
                        width: frame.width / 2.0,
                        height: frame.height
                    )
                    let rightFrame = CGRect(
                        x: frame.origin.x + frame.width / 2.0,
                        y: frame.origin.y,
                        width: frame.width / 2.0,
                        height: frame.height
                    )
                    traversalNodes.append((node: left, frame: leftFrame))
                    traversalNodes.append((node: right, frame: rightFrame))
                } else {
                    let topFrame = CGRect(
                        x: frame.origin.x,
                        y: frame.origin.y,
                        width: frame.width,
                        height: frame.height / 2.0
                    )
                    let bottomFrame = CGRect(
                        x: frame.origin.x,
                        y: frame.origin.y + frame.height / 2.0,
                        width: frame.width,
                        height: frame.height / 2.0
                    )
                    traversalNodes.append((node: left, frame: topFrame))
                    traversalNodes.append((node: right, frame: bottomFrame))
                }
            }
        }

        return ret
    }
}

extension BinarySpacePartitioningLayout: Equatable {
    static func == (lhs: BinarySpacePartitioningLayout<Window>, rhs: BinarySpacePartitioningLayout<Window>) -> Bool {
        return lhs.rootNode == rhs.rootNode
    }
}


================================================
FILE: Amethyst/Layout/Layouts/ColumnLayout.swift
================================================
//
//  ColumnLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class ColumnLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Column" }
    override static var layoutKey: String { return "column" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count - mainPaneCount
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()
        let mainPaneWidth = round(screenFrame.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))
        let mainPaneWindowWidth = round(mainPaneWidth / CGFloat(mainPaneCount))
        let secondaryPaneWindowWidth = hasSecondaryPane ? round((screenFrame.width - mainPaneWidth) / CGFloat(secondaryPaneCount)) : 0.0

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame: CGRect = .zero
            let isMain = frameAssignments.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.width / mainPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x + (mainPaneWindowWidth * CGFloat(frameAssignments.count))
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = screenFrame.height
            } else {
                scaleFactor = (screenFrame.width / secondaryPaneWindowWidth) / CGFloat(secondaryPaneCount)
                windowFrame.origin.x = screenFrame.origin.x + mainPaneWidth + (secondaryPaneWindowWidth * CGFloat(frameAssignments.count - mainPaneCount))
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = screenFrame.height
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )
            let operation = FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)

            assignments.append(operation)

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/CustomLayout.swift
================================================
//
//  CustomLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 7/2/21.
//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.
//

import CommonCrypto
import Foundation
import JavaScriptCore

private struct JSWindow<Window: WindowType> {
    let id: String
    let window: LayoutWindow<Window>
}

private extension JSValue {
    func toRoundedRect() -> CGRect {
        let rect = toRect()
        return CGRect(x: round(rect.origin.x), y: round(rect.origin.y), width: round(rect.width), height: round(rect.height))
    }
}

private enum LayoutExtension<Window: WindowType> {
    case none
    case layout(Layout<Window>)
}

class CustomLayout<Window: WindowType>: StatefulLayout<Window>, PanedLayout {
    typealias WindowID = Window.WindowID

    private enum CodingKeys: String, CodingKey {
        case key
        case fileURL
    }

    override static var layoutName: String { return "Custom" }
    override static var layoutKey: String { return "custom" }

    override var layoutKey: String {
        return key
    }

    override var layoutName: String {
        return layout?.objectForKeyedSubscript("name").toString() ?? layoutKey
    }

    var mainPaneRatio: CGFloat { return 1.0 }
    var mainPaneCount: Int { return 1 }

    private let key: String
    private let fileURL: URL

    private lazy var context: JSContext? = {
        guard let context = JSContext() else {
            log.error("Failed to create javascript context")
            return nil
        }

        context.exceptionHandler = { (_: JSContext!, value: JSValue!) in
            let name = value.objectForKeyedSubscript("name").toString() ?? ""
            let message = value.objectForKeyedSubscript("message").toString() ?? ""
            let stack = value.objectForKeyedSubscript("stack").toString() ?? ""
            log.error("\(name): \(message)\n\(stack)")
        }

        context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")

        let consoleLog: @convention(block) (String) -> Void = { message in
            log.debug(message)
        }
        context.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol))

        do {
            context.evaluateScript(try String(contentsOf: self.fileURL))
        } catch {
            log.error(error)
            return nil
        }

        context.evaluateScript("""
        function sanitizeArguments(fn) {
            return function(...args) {
                const sanitizedArgs = args.map(arg => !!arg ? JSON.parse(JSON.stringify(arg)) : undefined);
                return fn(...sanitizedArgs);
            };
        }

        function normalizedLayout() {
            const l = layout();
            l.getFrameAssignments = sanitizeArguments(l.getFrameAssignments);
            return l;
        }
        """)

        return context
    }()

    private lazy var layout: JSValue? = {
        return self.context?.objectForKeyedSubscript("normalizedLayout")?.call(withArguments: [])
    }()

    private lazy var state: JSValue? = {
        return self.layout?.objectForKeyedSubscript("initialState")
    }()

    private lazy var commands: JSValue? = {
        return self.layout?.objectForKeyedSubscript("commands")
    }()

    private lazy var layoutExtension: LayoutExtension<Window> = {
        guard let extendedLayoutKey = self.layout?.objectForKeyedSubscript("extends"), extendedLayoutKey.isString else {
            return .none
        }

        guard let layout = LayoutType<Window>.layoutForKey(extendedLayoutKey.toString()) else {
            return .none
        }

        return .layout(layout)
    }()

    required init() {
        fatalError("must be constructed with a file")
    }

    required init(key: String, fileURL: URL) {
        self.key = key
        self.fileURL = fileURL
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.key = try values.decode(String.self, forKey: .key)
        self.fileURL = try values.decode(URL.self, forKey: .fileURL)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(key, forKey: .key)
        try container.encode(fileURL, forKey: .fileURL)
    }

    private func extendedFrameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        switch layoutExtension {
        case .none:
            return nil
        case .layout(let layout):
            return layout.frameAssignments(windowSet, on: screen)
        }
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let screenFrame = screen.adjustedFrame()
        let jsScreenFrameArg = JSValue(rect: screenFrame, in: context)!
        let jsWindows: [WindowID: JSWindow<Window>] = windows.reduce([:]) { partialResult, layoutWindow in
            let id = idHash(forWindowID: layoutWindow.id) ?? UUID().uuidString
            let window = JSWindow<Window>(id: id, window: layoutWindow)
            return partialResult.merging([layoutWindow.id: window]) { current, _ in return current }
        }
        let jsWindowsArg = windows.map { window -> [String: Any?] in
            let jsWindow = jsWindows[window.id]!
            return [
                "id": jsWindow.id,
                "frame": JSValue(rect: jsWindow.window.frame, in: context),
                "isFocused": jsWindow.window.isFocused
            ]
        }

        let extendedFrames: [[String: Any?]]? = extendedFrameAssignments(windowSet, on: screen)?.compactMap { frameAssignmentOperation in
            let frameAssignment = frameAssignmentOperation.frameAssignment
            guard let jsWindow = jsWindows[frameAssignment.window.id] else {
                return nil
            }
            return [
                "id": jsWindow.id,
                "frame": JSValue(rect: frameAssignment.frame, in: context),
                "isFocused": jsWindow.window.isFocused
            ]
        }
        let args: [Any] = [
            jsWindowsArg,
            jsScreenFrameArg,
            state ?? JSValue(undefinedIn: context)!,
            extendedFrames ?? JSValue(undefinedIn: context)!
        ]

        guard let getAssignments = layout?.objectForKeyedSubscript("getFrameAssignments"), !getAssignments.isNull && !getAssignments.isUndefined else {
            return nil
        }

        guard let assignments = getAssignments.call(withArguments: args), assignments.isObject else {
            return nil
        }

        return windows.compactMap { window -> FrameAssignmentOperation<Window>? in
            guard let jsWindow = jsWindows[window.id] else {
                return nil
            }

            guard let frame = assignments.objectForKeyedSubscript(jsWindow.id) else {
                return nil
            }

            var unconstrainedDimension: UnconstrainedDimension = .horizontal
            var scaleFactor = screenFrame.width / frame.toRoundedRect().width

            if let dimension = frame.objectForKeyedSubscript("unconstrainedDimension")?.toString() {
                switch dimension {
                case "horizontal":
                    unconstrainedDimension = .horizontal
                case "vertical":
                    unconstrainedDimension = .vertical
                    scaleFactor = screenFrame.height / frame.toRoundedRect().height
                default:
                    log.warning("Encountered unknown unconstrainedDimension value: \(dimension), defaulting to horizontal")
                    unconstrainedDimension = .horizontal
                }
            }

            let isMain = frame.objectForKeyedSubscript("isMain")?.toBool() ?? true
            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: unconstrainedDimension, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: frame.toRoundedRect(),
                window: jsWindow.window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )
            return FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)
        }
    }

    override func updateWithChange(_ windowChange: Change<Window>) {
        guard let updateWithChange = layout?.objectForKeyedSubscript("updateWithChange"), !updateWithChange.isNull && !updateWithChange.isUndefined else {
            return
        }

        let updateWithChangeArgs: [Any]? = state.flatMap { state in
            return [jsChange(forChange: windowChange), state]
        }

        guard let updatedState = updateWithChange.call(withArguments: updateWithChangeArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {
            log.error("\(layoutKey)): received invalid updated state")
            return
        }

        state = updatedState
    }

    func command1() {
        command(key: "command1")
    }

    func command2() {
        command(key: "command2")
    }

    func command3() {
        command(key: "command3")
    }

    func command4() {
        command(key: "command4")
    }

    override func nextWindowIDClockwise() -> Window.WindowID? {
        return nil
    }

    override func nextWindowIDCounterClockwise() -> Window.WindowID? {
        return nil
    }

    private func command(key: String) {
        guard let command = commands?.objectForKeyedSubscript(key), command.isObject else {
            log.debug("\(layoutKey) — \(key): no command defined")
            return
        }

        guard let updateState = command.objectForKeyedSubscript("updateState"), !updateState.isNull && !updateState.isUndefined else {
            log.debug("\(layoutKey) — \(key): no updateState function provided")
            return
        }

        let focusedWindowID = Window.currentlyFocused().flatMap { idHash(forWindowID: $0.id()) }
        let updateStateArgs: [Any]? = state.flatMap { state in
            if let id = focusedWindowID {
                return [state, id]
            } else {
                return [state]
            }
        }

        guard let updatedState = updateState.call(withArguments: updateStateArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {
            log.error("\(layoutKey) — \(key): received invalid updated state")
            return
        }

        state = updatedState
    }

    private func idHash(forWindowID windowID: WindowID) -> String? {
        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .sortedKeys

            let encodedID = try encoder.encode(windowID)
            var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
            encodedID.withUnsafeBytes {
                _ = CC_SHA256($0.baseAddress, CC_LONG(encodedID.count), &hash)
            }
            return hash.map { String(format: "%02hhx", $0) }.joined()
        } catch {
            log.warning("Failed to hash window id: \(error)")
            return nil
        }
    }

    private func jsChange(forChange change: Change<Window>) -> [String: String] {
        var jsChange: [String: String] = [:]

        switch change {
        case .add(window: let window):
            jsChange["change"] = "add"
            jsChange["windowID"] = idHash(forWindowID: window.id())
        case .remove(window: let window):
            jsChange["change"] = "remove"
            jsChange["windowID"] = idHash(forWindowID: window.id())
        case .focusChanged(window: let window):
            jsChange["change"] = "focus_changed"
            jsChange["windowID"] = idHash(forWindowID: window.id())
        case .windowSwap(window: let window, otherWindow: let otherWindow):
            jsChange["change"] = "window_swap"
            jsChange["windowID"] = idHash(forWindowID: window.id())
            jsChange["otherWindowID"] = idHash(forWindowID: otherWindow.id())
        case .applicationActivate:
            jsChange["change"] = "application_activate"
        case .applicationDeactivate:
            jsChange["change"] = "application_deactivate"
        case .spaceChange:
            jsChange["change"] = "space_change"
        case .layoutChange:
            jsChange["change"] = "layout_change"
        case .tabChange:
            jsChange["change"] = "tab_change"
        case .unknown:
            jsChange["change"] = "unknown"
        case .none:
            jsChange["change"] = "none"
        }

        return jsChange
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        guard
            let recommendMainPaneRatio = layout?.objectForKeyedSubscript("recommendMainPaneRatio"),
            !recommendMainPaneRatio.isNull && !recommendMainPaneRatio.isUndefined
        else {
            return
        }

        let recommendMainPaneRatioArgs: [Any]? =  state.flatMap { [rawRatio, $0] }

        guard let updatedState = recommendMainPaneRatio.call(withArguments: recommendMainPaneRatioArgs ?? []), !updatedState.isNull && !updatedState.isUndefined else {
            log.error("\(layoutKey) — recommendMainPaneRawRatio: received invalid updated state")
            return
        }

        state = updatedState
    }

    func increaseMainPaneCount() {
        command(key: "increaseMain")
    }

    func decreaseMainPaneCount() {
        command(key: "decreaseMain")
    }

    func shrinkMainPane() {
        command(key: "shrinkMain")
    }

    func expandMainPane() {
        command(key: "expandMain")
    }
}


================================================
FILE: Amethyst/Layout/Layouts/FloatingLayout.swift
================================================
//
//  FloatingLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class FloatingLayout<Window: WindowType>: Layout<Window> {
    override static var layoutName: String { return "Floating" }
    override static var layoutKey: String { return "floating" }

    override var layoutDescription: String { return "" }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        return nil
    }
}


================================================
FILE: Amethyst/Layout/Layouts/FourColumnLayout.swift
================================================
//
//  FourColumnLayout.swift
//  Amethyst
//
//  Originally created by Ian Ynda-Hummel on 12/15/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//
//  Modifications by Craig Disselkoen on 09/03/18.
//  Modifications by Reyk Floeter on 10/28/21.
//

import Silica

// we'd like to hide these structures and enums behind fileprivate, but
// https://bugs.swift.org/browse/SR-47

enum FourColumn {
    case left
    case middleLeft
    case middleRight
    case right
}

enum FourPane {
    case main
    case secondary
    case tertiary
    case quaternary
}

struct FourPaneWidths {
    var left: CGFloat = 0
    var middleLeft: CGFloat = 0
    var middleRight: CGFloat = 0
    var right: CGFloat = 0
}

struct QuadruplePaneArrangement {
    /// number of windows in pane
    private let paneCount: [FourPane: UInt]

    /// height of windows in pane
    private let paneWindowHeight: [FourPane: CGFloat]

    /// width of windows in pane
    private let paneWindowWidth: [FourPane: CGFloat]

    // how panes relate to columns
    private let panePosition: [FourPane: FourColumn]

    /// how columns relate to panes
    private let columnDesignation: [FourColumn: FourPane]

    /**
     - Parameters:
        - mainPane: which Column is the main Pane
        - numWindows: how many windows total
        - numMainPane: how many windows in the main Pane
        - screenSize: total size of the screen
        - mainPaneRatio: ratio of the screen taken by main pane
     */
    init(mainPane: FourColumn, numWindows: UInt, numMainPane: UInt, screenSize: CGSize, mainPaneRatio: CGFloat) {
        // forward and reverse mapping of columns to their designations
        self.panePosition = {
            switch mainPane {
            case .left: return [.main: .left, .secondary: .middleLeft, .tertiary: .middleRight, .quaternary: .right]
            case .middleLeft: return [.main: .middleLeft, .secondary: .middleRight, .tertiary: .left, .quaternary: .right]
            case .middleRight: return [.main: .middleRight, .secondary: .middleLeft, .tertiary: .right, .quaternary: .left]
            case .right: return [.main: .right, .secondary: .middleRight, .tertiary: .middleLeft, .quaternary: .left]
            }
        }()
        // swap keys and values for reverse lookup
        self.columnDesignation = Dictionary(uniqueKeysWithValues: panePosition.map({ ($1, $0) }))

        // calculate how many are in each type
        let mainPaneCount = min(numWindows, numMainPane)
        let nonMainCount: UInt = numWindows - mainPaneCount
        // we do tertiary first because a single window produces a zero in integer division by 2
        let nonMainPaneCount: UInt = max(nonMainCount / 3, 1)
        let quaternaryPaneCount = nonMainPaneCount
        let tertiaryPaneCount = nonMainPaneCount
        let secondaryPaneCount = nonMainPaneCount + max(nonMainCount, 3) % 3
        self.paneCount = [.main: mainPaneCount, .secondary: secondaryPaneCount, .tertiary: tertiaryPaneCount, .quaternary: quaternaryPaneCount]

        // calculate heights
        let screenHeight = screenSize.height
        self.paneWindowHeight = [
            .main: round(screenHeight / CGFloat(mainPaneCount)),
            .secondary: secondaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(secondaryPaneCount)),
            .tertiary: tertiaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(tertiaryPaneCount)),
            .quaternary: quaternaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(quaternaryPaneCount))
        ]

        // calculate widths
        let screenWidth = screenSize.width
        let mainWindowWidth = round(screenWidth / 4)
        let nonMainWindowWidth = round(screenWidth / 4)
        self.paneWindowWidth = [
            .main: mainWindowWidth,
            .secondary: nonMainWindowWidth,
            .tertiary: nonMainWindowWidth,
            .quaternary: nonMainWindowWidth
        ]
   }

    func count(_ pane: FourPane) -> UInt {
        return paneCount[pane]!
    }

    func height(_ pane: FourPane) -> CGFloat {
        return paneWindowHeight[pane]!
    }

    func width(_ pane: FourPane) -> CGFloat {
        return paneWindowWidth[pane]!
    }

    func firstIndex(_ pane: FourPane) -> UInt {
        switch pane {
        case .main: return 0
        case .secondary: return count(.main)
        case .tertiary: return count(.main) + count(.secondary)
        case .quaternary: return count(.main) + count(.secondary) + count(.tertiary)
        }
    }

    func pane(ofIndex windowIndex: UInt) -> FourPane {
        if windowIndex >= firstIndex(.quaternary) {
            return .quaternary
        }
        if windowIndex >= firstIndex(.tertiary) {
            return .tertiary
        }
        if windowIndex >= firstIndex(.secondary) {
            return .secondary
        }
        return .main
    }

    /// Given a window index, which Pane does it belong to, and which index within that Pane
    func coordinates(at windowIndex: UInt) -> (FourPane, UInt) {
        let pane = self.pane(ofIndex: windowIndex)
        return (pane, windowIndex - firstIndex(pane))
    }

    /// Get the (height, width) dimensions for a window in the given Pane
    func windowDimensions(inPane pane: FourPane) -> (CGFloat, CGFloat) {
        return (height(pane), width(pane))
    }

    /// Get the Column assignment for the given Pane
    func column(ofPane pane: FourPane) -> FourColumn {
        return panePosition[pane]!
    }

    func pane(ofColumn column: FourColumn) -> FourPane {
        return columnDesignation[column]!
    }

    /// Get the column widths in the order (left, middle, right)
    func widthsLeftToRight() -> FourPaneWidths {
        return FourPaneWidths(
            left: width(pane(ofColumn: .left)),
            middleLeft: width(pane(ofColumn: .middleLeft)),
            middleRight: width(pane(ofColumn: .middleRight)),
            right: width(pane(ofColumn: .right))
        )
    }
}

// not an actual Layout, just a base class for the four actual Layouts below
class FourColumnLayout<Window: WindowType>: Layout<Window> {
    class var mainColumn: FourColumn { fatalError("Must be implemented by subclass") }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let screenFrame = screen.adjustedFrame()
        let paneArrangement = QuadruplePaneArrangement(
            mainPane: type(of: self).mainColumn,
            numWindows: UInt(windows.count),
            numMainPane: UInt(mainPaneCount),
            screenSize: screenFrame.size,
            mainPaneRatio: mainPaneRatio
        )

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame = CGRect.zero
            let windowIndex: UInt = UInt(frameAssignments.count)

            let (pane, paneIndex) = paneArrangement.coordinates(at: windowIndex)

            let (windowHeight, windowWidth): (CGFloat, CGFloat) = paneArrangement.windowDimensions(inPane: pane)
            let column: FourColumn = paneArrangement.column(ofPane: pane)

            let widths = paneArrangement.widthsLeftToRight()

            let xorigin: CGFloat = screenFrame.origin.x + {
                switch column {
                case .left: return 0.0
                case .middleLeft: return widths.left
                case .middleRight: return widths.left + widths.middleLeft
                case .right: return widths.left + widths.middleLeft + widths.middleRight
                }
            }()

            let scaleFactor: CGFloat = screenFrame.width / {
                if pane == .main {
                    return paneArrangement.width(.main)
                }
                return paneArrangement.width(.secondary) + paneArrangement.width(.tertiary) + paneArrangement.width(.quaternary)
            }()

            windowFrame.origin.x = xorigin
            windowFrame.origin.y = screenFrame.origin.y + (windowHeight * CGFloat(paneIndex))
            windowFrame.size.width = windowWidth
            windowFrame.size.height = windowHeight

            let isMain = windowIndex < paneArrangement.firstIndex(.secondary)

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}

extension FourColumnLayout {
    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }
}

// implement the two variants
class FourColumnLeftLayout<Window: WindowType>: FourColumnLayout<Window>, PanedLayout {
    override static var layoutName: String { return "4Column Left" }
    override static var layoutKey: String { return "4column-left" }
    override static var mainColumn: FourColumn { return .middleLeft }
}

class FourColumnRightLayout<Window: WindowType>: FourColumnLayout<Window>, PanedLayout {
    override static var layoutName: String { return "4Column Right" }
    override static var layoutKey: String { return "4column-right" }
    override static var mainColumn: FourColumn { return .middleRight }
}


================================================
FILE: Amethyst/Layout/Layouts/FullscreenLayout.swift
================================================
//
//  FullscreenLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class FullscreenLayout<Window: WindowType>: Layout<Window> {
    override static var layoutName: String { return "Fullscreen" }
    override static var layoutKey: String { return "fullscreen" }

    override var layoutDescription: String { return "" }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let screenFrame = screen.adjustedFrame(disableWindowMargins: UserConfiguration.shared.smartWindowMargins())
        return windowSet.windows.map { window in
            let resizeRules = ResizeRules(isMain: true, unconstrainedDimension: .horizontal, scaleFactor: 1)
            let frameAssignment = FrameAssignment<Window>(
                frame: screenFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules,
                disableWindowMargins: UserConfiguration.shared.smartWindowMargins()
            )
            return FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/RowLayout.swift
================================================
//
//  RowLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class RowLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Row" }
    override static var layoutKey: String { return "row" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count - mainPaneCount
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()

        let mainPaneHeight = floor(screenFrame.size.height * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))
        let mainPaneWindowHeight = floor(mainPaneHeight / CGFloat(mainPaneCount))
        let secondaryPaneWindowHeight = hasSecondaryPane ? floor((screenFrame.size.height - mainPaneHeight) / CGFloat(secondaryPaneCount)) : 0.0

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame: CGRect = .zero
            let isMain = frameAssignments.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.size.height / mainPaneWindowHeight
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(frameAssignments.count))
                windowFrame.size.width = screenFrame.width
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.size.height / secondaryPaneWindowHeight / CGFloat(secondaryPaneCount)
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(mainPaneCount)) + (secondaryPaneWindowHeight * CGFloat(frameAssignments.count - mainPaneCount))
                windowFrame.size.width = screenFrame.width
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .vertical, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/TallLayout.swift
================================================
//
//  TallLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class TallLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Tall" }
    override static var layoutKey: String { return "tall" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    override var layoutDescription: String { return "" }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count - mainPaneCount
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()

        let mainPaneWindowHeight = round(screenFrame.size.height / CGFloat(mainPaneCount))
        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.size.height / CGFloat(secondaryPaneCount)) : 0.0

        let mainPaneWindowWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))
        let secondaryPaneWindowWidth = screenFrame.size.width - mainPaneWindowWidth

        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in
            var assignments = acc
            var windowFrame = CGRect.zero
            let isMain = acc.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.size.width / mainPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(acc.count))
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x + mainPaneWindowWidth
                windowFrame.origin.y = screenFrame.origin.y + (secondaryPaneWindowHeight * CGFloat(acc.count - mainPaneCount))
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/TallRightLayout.swift
================================================
//
//  TallRightLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class TallRightLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Tall Right" }
    override static var layoutKey: String { return "tall-right" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count - mainPaneCount
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()

        let mainPaneWindowHeight = round(screenFrame.size.height / CGFloat(mainPaneCount))
        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.size.height / CGFloat(secondaryPaneCount)) : 0.0

        let secondaryPaneWindowWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(1.0 - mainPaneRatio) : 0))
        let mainPaneWindowWidth = screenFrame.size.width - secondaryPaneWindowWidth

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame = CGRect.zero
            let isMain = frameAssignments.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.size.width / mainPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x + secondaryPaneWindowWidth
                windowFrame.origin.y = screenFrame.origin.y + (mainPaneWindowHeight * CGFloat(frameAssignments.count))
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y + secondaryPaneWindowHeight * CGFloat(windows.count - (frameAssignments.count + 1))
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/ThreeColumnLayout.swift
================================================
//
//  ThreeColumnLayout.swift
//  Amethyst
//
//  Originally created by Ian Ynda-Hummel on 12/15/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//
//  Modifications by Craig Disselkoen on 09/03/18.
//

import Silica

// we'd like to hide these structures and enums behind fileprivate, but
// https://bugs.swift.org/browse/SR-47

enum Column {
    case left
    case middle
    case right
}

enum Pane {
    case main
    case secondary
    case tertiary
}

struct TriplePaneArrangement {
    /// number of windows in pane
    private let paneCount: [Pane: UInt]

    /// height of windows in pane
    private let paneWindowHeight: [Pane: CGFloat]

    /// width of windows in pane
    private let paneWindowWidth: [Pane: CGFloat]

    // how panes relate to columns
    private let panePosition: [Pane: Column]

    /// how columns relate to panes
    private let columnDesignation: [Column: Pane]

    /**
     - Parameters:
        - mainPane: which Column is the main Pane
        - numWindows: how many windows total
        - numMainPane: how many windows in the main Pane
        - screenSize: total size of the screen
        - mainPaneRatio: ratio of the screen taken by main pane
     */
    init(mainPane: Column, numWindows: UInt, numMainPane: UInt, screenSize: CGSize, mainPaneRatio: CGFloat) {
        // forward and reverse mapping of columns to their designations
        self.panePosition = {
            switch mainPane {
            case .left:   return [.main: .left, .secondary: .middle, .tertiary: .right]
            case .middle: return [.main: .middle, .secondary: .left, .tertiary: .right]
            case .right:  return [.main: .right, .secondary: .left, .tertiary: .middle]
            }
        }()
        // swap keys and values for reverse lookup
        self.columnDesignation = Dictionary(uniqueKeysWithValues: panePosition.map({ ($1, $0) }))

        // calculate how many are in each type
        let mainPaneCount = min(numWindows, numMainPane)
        let nonMainCount: UInt = numWindows - mainPaneCount
        // we do tertiary first because a single window produces a zero in integer division by 2
        let tertiaryPaneCount = nonMainCount >> 1
        let secondaryPaneCount = nonMainCount - tertiaryPaneCount
        self.paneCount = [.main: mainPaneCount, .secondary: secondaryPaneCount, .tertiary: tertiaryPaneCount]

        // calculate heights
        let screenHeight = screenSize.height
        self.paneWindowHeight = [
            .main: round(screenHeight / CGFloat(mainPaneCount)),
            .secondary: secondaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(secondaryPaneCount)),
            .tertiary: tertiaryPaneCount == 0 ? 0.0 : round(screenHeight / CGFloat(tertiaryPaneCount))
        ]

        // calculate widths
        let screenWidth = screenSize.width
        let mainWindowWidth = secondaryPaneCount == 0 ? screenWidth : round(screenWidth * mainPaneRatio)
        let nonMainWindowWidth = round((screenWidth - mainWindowWidth) / 2)
        self.paneWindowWidth = [
            .main: mainWindowWidth,
            .secondary: nonMainWindowWidth,
            .tertiary: nonMainWindowWidth
        ]
   }

    func count(_ pane: Pane) -> UInt {
        return paneCount[pane]!
    }

    func height(_ pane: Pane) -> CGFloat {
        return paneWindowHeight[pane]!
    }

    func width(_ pane: Pane) -> CGFloat {
        return paneWindowWidth[pane]!
    }

    func firstIndex(_ pane: Pane) -> UInt {
        switch pane {
        case .main: return 0
        case .secondary: return count(.main)
        case .tertiary: return count(.main) + count(.secondary)
        }
    }

    func pane(ofIndex windowIndex: UInt) -> Pane {
        if windowIndex >= firstIndex(.tertiary) {
            return .tertiary
        }
        if windowIndex >= firstIndex(.secondary) {
            return .secondary
        }
        return .main
    }

    /// Given a window index, which Pane does it belong to, and which index within that Pane
    func coordinates(at windowIndex: UInt) -> (Pane, UInt) {
        let pane = self.pane(ofIndex: windowIndex)
        return (pane, windowIndex - firstIndex(pane))
    }

    /// Get the (height, width) dimensions for a window in the given Pane
    func windowDimensions(inPane pane: Pane) -> (CGFloat, CGFloat) {
        return (height(pane), width(pane))
    }

    /// Get the Column assignment for the given Pane
    func column(ofPane pane: Pane) -> Column {
        return panePosition[pane]!
    }

    func pane(ofColumn column: Column) -> Pane {
        return columnDesignation[column]!
    }

    /// Get the column widths in the order (left, middle, right)
    func widthsLeftToRight() -> (CGFloat, CGFloat, CGFloat) {
        return (width(pane(ofColumn: .left)), width(pane(ofColumn: .middle)), width(pane(ofColumn: .right)))
    }
}

// not an actual Layout, just a base class for the three actual Layouts below
class ThreeColumnLayout<Window: WindowType>: Layout<Window> {
    class var mainColumn: Column { fatalError("Must be implemented by subclass") }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let screenFrame = screen.adjustedFrame()
        let paneArrangement = TriplePaneArrangement(
            mainPane: type(of: self).mainColumn,
            numWindows: UInt(windows.count),
            numMainPane: UInt(mainPaneCount),
            screenSize: screenFrame.size,
            mainPaneRatio: mainPaneRatio
        )

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame = CGRect.zero
            let windowIndex: UInt = UInt(frameAssignments.count)

            let (pane, paneIndex) = paneArrangement.coordinates(at: windowIndex)

            let (windowHeight, windowWidth): (CGFloat, CGFloat) = paneArrangement.windowDimensions(inPane: pane)
            let column: Column = paneArrangement.column(ofPane: pane)

            let (leftPaneWidth, middlePaneWidth, _): (CGFloat, CGFloat, CGFloat) = paneArrangement.widthsLeftToRight()

            let xorigin: CGFloat = screenFrame.origin.x + {
                switch column {
                case .left: return 0.0
                case .middle: return leftPaneWidth
                case .right: return leftPaneWidth + middlePaneWidth
                }
            }()

            let scaleFactor: CGFloat = screenFrame.width / {
                if pane == .main {
                    return paneArrangement.width(.main)
                }
                return paneArrangement.width(.secondary) + paneArrangement.width(.tertiary)
            }()

            windowFrame.origin.x = xorigin
            windowFrame.origin.y = screenFrame.origin.y + (windowHeight * CGFloat(paneIndex))
            windowFrame.size.width = windowWidth
            windowFrame.size.height = windowHeight

            let isMain = windowIndex < paneArrangement.firstIndex(.secondary)

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}

extension ThreeColumnLayout {
    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }
}

// implement the three variants

class ThreeColumnLeftLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {
    override static var layoutName: String { return "3Column Left" }
    override static var layoutKey: String { return "3column-left" }
    override static var mainColumn: Column { return .left }
}

class ThreeColumnMiddleLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {
    override static var layoutName: String { return "3Column Middle" }
    // for backwards compatibility with users who still have 'middle-wide' in their active layouts
    override static var layoutKey: String { return "middle-wide" }
    override static var mainColumn: Column { return .middle }
}

class ThreeColumnRightLayout<Window: WindowType>: ThreeColumnLayout<Window>, PanedLayout {
    override static var layoutName: String { return "3Column Right" }
    override static var layoutKey: String { return "3column-right" }
    override static var mainColumn: Column { return .right }
}


================================================
FILE: Amethyst/Layout/Layouts/TwoPaneLayout.swift
================================================
//
//  TwoPaneLayout.swift
//  Amethyst
//
//  Created by @mwz on 10/06/2021.
//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class TwoPaneLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Two Pane" }
    override static var layoutKey: String { return "two-pane" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    override var layoutDescription: String { return "" }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {}

    func decreaseMainPaneCount() {}

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count > 1 ? 1 : 0
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()
        let isHorizontal = (screenFrame.size.width / screenFrame.size.height) >= 1

        let mainPaneWindowHeight = screenFrame.size.height * (!isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)
        let secondaryPaneWindowHeight = isHorizontal ? mainPaneWindowHeight : screenFrame.size.height - mainPaneWindowHeight

        let mainPaneWindowWidth = screenFrame.size.width * (isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)
        let secondaryPaneWindowWidth = !isHorizontal ? mainPaneWindowWidth : screenFrame.size.width - mainPaneWindowWidth

        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in
            var assignments = acc
            var windowFrame = CGRect.zero
            let isMain = acc.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.size.width / mainPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x + (isHorizontal ? mainPaneWindowWidth : 0)
                windowFrame.origin.y = screenFrame.origin.y + (isHorizontal ? 0 : mainPaneWindowHeight)
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/TwoPaneRightLayout.swift
================================================
//
//  TwoPaneRightLayout.swift
//  Amethyst
//
//  Created by Anja on 16.06.23.
//  Copyright © 2023 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class TwoPaneRightLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Two Pane Right" }
    override static var layoutKey: String { return "two-pane-right" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    override var layoutDescription: String { return "" }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {}

    func decreaseMainPaneCount() {}

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count > 1 ? 1 : 0
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()
        let isHorizontal = (screenFrame.size.width / screenFrame.size.height) >= 1

        let mainPaneWindowHeight = screenFrame.size.height * (!isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)
        let secondaryPaneWindowHeight = isHorizontal ? mainPaneWindowHeight : screenFrame.size.height - mainPaneWindowHeight

        let mainPaneWindowWidth = screenFrame.size.width * (isHorizontal && hasSecondaryPane ? mainPaneRatio : 1)
        let secondaryPaneWindowWidth = !isHorizontal ? mainPaneWindowWidth : screenFrame.size.width - mainPaneWindowWidth

        return windows.reduce([]) { acc, window -> [FrameAssignmentOperation<Window>] in
            var assignments = acc
            var windowFrame = CGRect.zero
            let isMain = acc.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.size.width / mainPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x + (isHorizontal ? secondaryPaneWindowWidth : 0)
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.size.width / secondaryPaneWindowWidth
                windowFrame.origin.x = screenFrame.origin.x
                windowFrame.origin.y = screenFrame.origin.y + (isHorizontal ? 0 : mainPaneWindowHeight)
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/WideLayout.swift
================================================
//
//  WideLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/14/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class WideLayout<Window: WindowType>: Layout<Window>, PanedLayout {
    override static var layoutName: String { return "Wide" }
    override static var layoutKey: String { return "wide" }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        guard !windows.isEmpty else {
            return []
        }

        let secondaryPaneCount = windows.count - mainPaneCount
        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()

        let mainPaneWindowHeight = round(screenFrame.height * CGFloat(hasSecondaryPane ? mainPaneRatio : 1))
        let secondaryPaneWindowHeight = screenFrame.height - mainPaneWindowHeight

        let mainPaneWindowWidth = round(screenFrame.width / CGFloat(mainPaneCount))
        let secondaryPaneWindowWidth = hasSecondaryPane ? round(screenFrame.width / CGFloat(secondaryPaneCount)) : 0.0

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame = CGRect.zero
            let isMain = frameAssignments.count < mainPaneCount
            var scaleFactor: CGFloat

            if isMain {
                scaleFactor = screenFrame.height / mainPaneWindowHeight
                windowFrame.origin.x = screenFrame.origin.x + (mainPaneWindowWidth * CGFloat(frameAssignments.count))
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = screenFrame.height / secondaryPaneWindowHeight
                windowFrame.origin.x = screenFrame.origin.x + (secondaryPaneWindowWidth * CGFloat(frameAssignments.count - mainPaneCount))
                windowFrame.origin.y = screenFrame.origin.y + mainPaneWindowHeight
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .vertical, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )
            let operation = FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet)

            assignments.append(operation)

            return assignments
        }
    }
}


================================================
FILE: Amethyst/Layout/Layouts/WidescreenTallLayout.swift
================================================
//
//  WidescreenTallLayout.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/15/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Silica

class WidescreenTallLayout<Window: WindowType>: Layout<Window> {
    class var isRight: Bool { fatalError("Must be implemented by subclass") }

    enum CodingKeys: String, CodingKey {
        case mainPaneCount
        case mainPaneRatio
    }

    private(set) var mainPaneCount: Int = 1
    private(set) var mainPaneRatio: CGFloat = 0.5

    required init() {
        super.init()
    }

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.mainPaneCount = try values.decode(Int.self, forKey: .mainPaneCount)
        self.mainPaneRatio = try values.decode(CGFloat.self, forKey: .mainPaneRatio)
        super.init()
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(mainPaneCount, forKey: .mainPaneCount)
        try container.encode(mainPaneRatio, forKey: .mainPaneRatio)
    }

    override func frameAssignments(_ windowSet: WindowSet<Window>, on screen: Screen) -> [FrameAssignmentOperation<Window>]? {
        let windows = windowSet.windows

        if windows.count == 0 {
            return []
        }

        let mainPaneCount = min(windows.count, self.mainPaneCount)
        let secondaryPaneCount = windows.count - mainPaneCount

        let hasSecondaryPane = secondaryPaneCount > 0

        let screenFrame = screen.adjustedFrame()

        let mainPaneWindowHeight = screenFrame.height
        let secondaryPaneWindowHeight = hasSecondaryPane ? round(screenFrame.height / CGFloat(secondaryPaneCount)) : 0.0

        let mainPaneWidth = round(screenFrame.size.width * (hasSecondaryPane ? CGFloat(mainPaneRatio) : 1.0))
        let mainPaneWindowWidth = round(mainPaneWidth / CGFloat(mainPaneCount))
        let secondaryPaneWindowWidth = screenFrame.width - mainPaneWidth

        return windows.reduce([]) { frameAssignments, window -> [FrameAssignmentOperation<Window>] in
            var assignments = frameAssignments
            var windowFrame = CGRect.zero
            let windowIndex = frameAssignments.count
            let isMain = windowIndex < mainPaneCount
            let scaleFactor: CGFloat

            if isMain {
                scaleFactor = CGFloat(screenFrame.size.width / mainPaneWindowWidth) / CGFloat(mainPaneCount)
                windowFrame.origin.x = screenFrame.origin.x + mainPaneWindowWidth * CGFloat(windowIndex)
                if type(of: self).isRight {
                    windowFrame.origin.x += secondaryPaneWindowWidth
                }
                windowFrame.origin.y = screenFrame.origin.y
                windowFrame.size.width = mainPaneWindowWidth
                windowFrame.size.height = mainPaneWindowHeight
            } else {
                scaleFactor = CGFloat(screenFrame.size.width / secondaryPaneWindowWidth)
                windowFrame.origin.x = screenFrame.origin.x + mainPaneWidth
                windowFrame.origin.y = screenFrame.origin.y + (secondaryPaneWindowHeight * CGFloat(windowIndex - mainPaneCount))
                windowFrame.size.width = secondaryPaneWindowWidth
                windowFrame.size.height = secondaryPaneWindowHeight
                if type(of: self).isRight {
                    windowFrame.origin.x = screenFrame.origin.x
                }
            }

            let resizeRules = ResizeRules(isMain: isMain, unconstrainedDimension: .horizontal, scaleFactor: scaleFactor)
            let frameAssignment = FrameAssignment<Window>(
                frame: windowFrame,
                window: window,
                screenFrame: screenFrame,
                resizeRules: resizeRules
            )

            assignments.append(FrameAssignmentOperation(frameAssignment: frameAssignment, windowSet: windowSet))

            return assignments
        }
    }
}

extension WidescreenTallLayout: PanedLayout {
    func recommendMainPaneRawRatio(rawRatio: CGFloat) {
        mainPaneRatio = rawRatio
    }

    func increaseMainPaneCount() {
        mainPaneCount += 1
    }

    func decreaseMainPaneCount() {
        mainPaneCount = max(1, mainPaneCount - 1)
    }
}

class WidescreenTallLayoutLeft<Window: WindowType>: WidescreenTallLayout<Window> {
    override class var isRight: Bool { return false }
    override static var layoutName: String { return "Widescreen Tall" }
    override static var layoutKey: String { return "widescreen-tall" }
}

class WidescreenTallLayoutRight<Window: WindowType>: WidescreenTallLayout<Window> {
    override class var isRight: Bool { return true }
    override static var layoutName: String { return "Widescreen Tall Right" }
    override static var layoutKey: String { return "widescreen-tall-right" }
}


================================================
FILE: Amethyst/Layout/ReflowOperation.swift
================================================
//
//  ReflowOperation.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 3/19/19.
//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.
//

import Foundation
import Silica

/// Possible dimensions without constraints.
enum UnconstrainedDimension: Int {
    /// The dimension along the x-axis.
    case horizontal

    /// The dimension along the y-axis.
    case vertical
}

/**
 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).

 Some window resizes reflect valid adjustments to the frame layout.
 
 Some window resizes would not be allowed due to hard constraints.
 */
struct ResizeRules {
    /// Whether or not the resize rule is applying to the main frame.
    let isMain: Bool

    /// The dimension that is allowed to scale.
    let unconstrainedDimension: UnconstrainedDimension

    /// the scale factor for the unconstrained dimension.
    let scaleFactor: CGFloat

    /**
     Determines the new value of the dimension based on the scale factor.
     
     Given a new frame, decide which dimension will be honored and return its size.
     
     - Parameters:
        - frame: The frame to transform.
        - negatePadding: Whether or not to take padding into account.
     */
    func scaledDimension(_ frame: CGRect, negatePadding: Bool) -> CGFloat {
        let dimension: CGFloat = {
            switch unconstrainedDimension {
            case .horizontal: return frame.width
            case .vertical: return frame.height
            }
        }()

        let padding = UserConfiguration.shared.windowMargins() ? UserConfiguration.shared.windowMarginSize() : 0
        return negatePadding ? dimension + padding : dimension
    }
}

struct LayoutWindow<Window: WindowType>: Equatable {
    let id: Window.WindowID
    let frame: CGRect
    let isFocused: Bool

    static func == (lhs: Self, rhs: Self) -> Bool {
        return lhs.id == rhs.id
    }
}

struct WindowSet<Window: WindowType> {
    let windows: [LayoutWindow<Window>]
    private let isWindowWithIDActive: (Window.WindowID) -> Bool
    private let isWindowWithIDFloating: (Window.WindowID) -> Bool
    private let windowForID: (Window.WindowID) -> Window?

    init(
        windows: [LayoutWindow<Window>],
        isWindowWithIDActive: @escaping (Window.WindowID) -> Bool,
        isWindowWithIDFloating: @escaping (Window.WindowID) -> Bool,
        windowForID: @escaping (Window.WindowID) -> Window?
    ) {
        self.windows = windows
        self.isWindowWithIDActive = isWindowWithIDActive
        self.isWindowWithIDFloating = isWindowWithIDFloating
        self.windowForID = windowForID
    }

    func isWindowActive(_ window: LayoutWindow<Window>) -> Bool {
        return isWindowWithIDActive(window.id)
    }

    func isWindowFloating(_ window: LayoutWindow<Window>) -> Bool {
        return isWindowWithIDFloating(window.id)
    }

    func perform(frameAssignment: FrameAssignment<Window>) {
        guard let window = windowForID(frameAssignment.window.id) else {
            return
        }

        guard isWindowWithIDActive(frameAssignment.window.id), !isWindowWithIDFloating(frameAssignment.window.id) else {
            return
        }

        frameAssignment.perform(withWindow: window)
    }
}

class FrameAssignmentOperation<Window: WindowType>: Operation {
    let frameAssignment: FrameAssignment<Window>
    let windowSet: WindowSet<Window>

    init(frameAssignment: FrameAssignment<Window>, windowSet: WindowSet<Window>) {
        self.frameAssignment = frameAssignment
        self.windowSet = windowSet
        super.init()
    }

    override func main() {
        guard !isCancelled else {
            return
        }

        windowSet.perform(frameAssignment: frameAssignment)
    }
}

/// Encapsulation of an assignment of a frame to a window.
struct FrameAssignment<Window: WindowType> {
    /// The frame to apply to the window.
    let frame: CGRect

    /// The window that will be moved and sized.
    let window: LayoutWindow<Window>

    /// The frame of the screen being occupied.
    let screenFrame: CGRect

    /// The rules governing constraints to frame transforms
    let resizeRules: ResizeRules

    /// If `true`, then  window margins won't be applied
    let disableWindowMargins: Bool

    init(frame: CGRect, window: LayoutWindow<Window>, screenFrame: CGRect, resizeRules: ResizeRules) {
        self.frame = frame
        self.window =  window
        self.screenFrame = screenFrame
        self.resizeRules = resizeRules
        self.disableWindowMargins = false
    }

    init(frame: CGRect, window: LayoutWindow<Window>, screenFrame: CGRect, resizeRules: ResizeRules, disableWindowMargins: Bool) {
        self.frame = frame
        self.window =  window
        self.screenFrame = screenFrame
        self.resizeRules = resizeRules
        self.disableWindowMargins = disableWindowMargins
    }

    /// The final frame is the desired frame, but transformed to provide desired padding
    var finalFrame: CGRect {
        var ret = frame
        let padding = floor(UserConfiguration.shared.windowMarginSize() / 2)

        if UserConfiguration.shared.windowMargins() && !disableWindowMargins {
            ret.origin.x += padding
            ret.origin.y += padding
            ret.size.width -= 2 * padding
            ret.size.height -= 2 * padding
        }

        let windowMinimumWidth = UserConfiguration.shared.windowMinimumWidth()
        let windowMinimumHeight = UserConfiguration.shared.windowMinimumHeight()

        if windowMinimumWidth > ret.size.width {
            ret.origin.x -= ((windowMinimumWidth - ret.size.width) / 2)
            ret.size.width = windowMinimumWidth
        }

        if windowMinimumHeight > ret.size.height {
            ret.origin.y -= ((windowMinimumHeight - ret.size.height) / 2)
            ret.size.height = windowMinimumHeight
        }

        return ret
    }

    /**
     Given a window frame and based on resizeRules, determine what the main pane ratio would be.
     
     This accounts for multiple main windows and primary vs non-primary being resized.
     
     - Parameters:
        - windowFrame: The frame of the window to test ratio against.
     
     - Returns:
     The estimate of the main pane ratio implied by how the frame would be transformed.
     */
    func impliedMainPaneRatio(windowFrame: CGRect) -> CGFloat {
        let oldDimension = resizeRules.scaledDimension(frame, negatePadding: false)
        let newDimension = resizeRules.scaledDimension(windowFrame, negatePadding: true)
        let implied =  (newDimension / oldDimension) / resizeRules.scaleFactor
        return resizeRules.isMain ? implied : 1 - implied
    }

    /// Perform the actual application of the frame to the window
    func perform(withWindow window: Window) {
        var finalFrame = self.finalFrame
        var finalOrigin = finalFrame.origin

        // If this is the focused window then we need to shift it to be on screen regardless of size
        // We call this "window peeking" (this line here to aid in text search)
        if window.isFocused() {
            // Just resize the window first to see what the dimensions end up being
            // Sometimes applications have internal window requirements that are not exposed to us directly
            finalFrame.origin = window.frame().origin
            DispatchQueue.main.sync {
                window.setFrame(finalFrame, withThreshold: CGSize(width: 1, height: 1))
            }

            // With the real height we can update the frame to account for the current size
            finalFrame.size = CGSize(
                width: max(window.frame().width, finalFrame.width),
                height: max(window.frame().height, finalFrame.height)
            )
            finalOrigin.x = max(screenFrame.minX, min(finalOrigin.x, screenFrame.maxX - finalFrame.size.width))
            finalOrigin.y = max(screenFrame.minY, min(finalOrigin.y, screenFrame.maxY - finalFrame.size.height))
        }

        // Move the window to its final frame
        finalFrame.origin = finalOrigin
        DispatchQueue.main.sync {
            window.setFrame(finalFrame, withThreshold: CGSize(width: 1, height: 1))
        }
    }
}


================================================
FILE: Amethyst/Managers/AppManager.swift
================================================
//
//  relaunch.swift
//  Amethyst
//
//  Created by Agustin Suarez on 2021-02-23.
//  Copyright © 2021 Ian Ynda-Hummel. All rights reserved.
//

import Foundation
import Cocoa

class AppManager {
    public static func relaunch() {
        let executablePath = Bundle.main.executablePath! as NSString
        let fileSystemRepresentedPath = executablePath.fileSystemRepresentation
        let fileSystemPath = FileManager.default.string(withFileSystemRepresentation: fileSystemRepresentedPath, length: Int(strlen(fileSystemRepresentedPath)))
        Process.launchedProcess(launchPath: fileSystemPath, arguments: [])
        NSApp.terminate(self)
    }
}


================================================
FILE: Amethyst/Managers/FocusFollowsMouseManager.swift
================================================
//
//  FocusFollowsMouseManager.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/15/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import Cocoa
import Foundation
import Silica
import RxSwift

protocol FocusFollowsMouseManagerDelegate: AnyObject {
    associatedtype Window: WindowType
    typealias Screen = Window.Screen
    func windows(onScreen screen: Screen) -> [Window]
}

class FocusFollowsMouseManager<Delegate: FocusFollowsMouseManagerDelegate> {
    typealias Window = Delegate.Window
    typealias Screen = Window.Screen

    weak var delegate: Delegate?

    private var lastMouseFocusTime = Date.distantPast

    private let userConfiguration: UserConfiguration
    private let disposeBag = DisposeBag()

    init(userConfiguration: UserConfiguration) {
        self.userConfiguration = userConfiguration

        // we want to observe changes to the focusFollowsMouse config, because mouse tracking has CPU cost
        UserDefaults.standard.rx.observe(Bool.self, ConfigurationKey.focusFollowsMouse.rawValue)
            .distinctUntilChanged { $0 == $1 }
            .scan(nil) { [unowned self] existingHandler, followingIsDesired -> Any? in
                if let handler = existingHandler {
                    NSEvent.removeMonitor(handler)
                }
                if followingIsDesired! {
                    return NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { [unowned self] event in
                        self.focusWindowWithMouseMovedEvent(event)
                    }
                } else {
                    return nil
                }
            }
            .subscribe()
            .disposed(by: disposeBag)
    }

    private func focusWindowWithMouseMovedEvent(_ event: NSEvent) {
        guard userConfiguration.focusFollowsMouse() else {
            log.warning("Subscribed to mouse move events that we are ignoring")
            return
        }

        guard let screen = Screen.availableScreens.first(where: { $0.frameIncludingDockAndMenu().contains(event.locationInWindow) }) else {
            return
        }

        guard let windows = delegate?.windows(onScreen: screen) else {
            return
        }

        var mousePoint = NSPointToCGPoint(event.locationInWindow)
        mousePoint.y = Screen.globalHeight() - mousePoint.y + screen.frameIncludingDockAndMenu().origin.y

        if let focusedWindow = Window.currentlyFocused() {
            // If the point is already in the frame of the focused window do nothing.
            guard !focusedWindow.frame().contains(mousePoint) else {
                return
            }
        }

        guard let topWindow = WindowsInformation.topWindowForScreenAtPoint(mousePoint, withWindows: windows) else {
            return
        }

        self.lastMouseFocusTime = Date()

        topWindow.focus()
    }

    func recentlyTriggeredFocusFollowsMouse() -> Bool {
        return Date().timeIntervalSince(lastMouseFocusTime) < 0.5
    }
}


================================================
FILE: Amethyst/Managers/FocusTransitionCoordinator.swift
================================================
//
//  FocusTransitionCoordinator.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 3/24/19.
//  Copyright © 2019 Ian Ynda-Hummel. All rights reserved.
//

import Cocoa
import Foundation
import Silica

enum FocusTransition<Window: WindowType> {
    typealias Screen = Window.Screen
    case focusWindow(_ window: Window)
    case focusScreen(_ screen: Screen)
}

protocol FocusTransitionTarget: AnyObject {
    associatedtype Application: ApplicationType
    typealias Window = Application.Window
    typealias Screen = Window.Screen

    func executeTransition(_ transition: FocusTransition<Window>)

    func lastFocusedWindow(on screen: Screen) -> Window?
    func screen(at index: Int) -> Screen?
    func windows(onScreen screen: Screen) -> [Window]
    func nextWindowIDClockwise(on screen: Screen) -> Window.WindowID?
    func nextWindowIDCounterClockwise(on screen: Screen) -> Window.WindowID?
    func nextScreenIndexClockwise(from screen: Screen) -> Int
    func nextScreenIndexCounterClockwise(from screen: Screen) -> Int
}

class FocusTransitionCoordinator<Target: FocusTransitionTarget> {
    typealias Window = Target.Window
    typealias Screen = Window.Screen

    weak var target: Target?

    private let userConfiguration: UserConfiguration
    private let focusFollowsMouseManager: FocusFollowsMouseManager<FocusTransitionCoordinator<Target>>

    init(userConfiguration: UserConfiguration) {
        self.userConfiguration = userConfiguration
        self.focusFollowsMouseManager = FocusFollowsMouseManager(userConfiguration: userConfiguration)
        self.focusFollowsMouseManager.delegate = self
    }

    func moveFocusCounterClockwise() {
        guard let focusedWindow = Window.currentlyFocused() else {
            focusScreen(at: 0)
            return
        }

        guard let screen = focusedWindow.screen() else {
            return
        }

        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {
            return
        }

        let windowToFocus = { () -> Window in
            if let nextWindowID = self.target?.nextWindowIDCounterClockwise(on: screen) {
                let windowToFocusIndex = windows.firstIndex { $0.id() == nextWindowID } ?? 0
                return windows[windowToFocusIndex]
            } else {
                let windowIndex = windows.firstIndex(of: focusedWindow) ?? 0
                let windowToFocusIndex = (windowIndex == 0 ? windows.count - 1 : windowIndex - 1)
                return windows[windowToFocusIndex]
            }
        }()

        windowToFocus.focus()
    }

    func moveFocusClockwise() {
        guard let focusedWindow = Window.currentlyFocused() else {
            focusScreen(at: 0)
            return
        }

        guard let screen = focusedWindow.screen() else {
            return
        }

        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {
            return
        }

        let windowToFocus = { () -> Window in
            if let nextWindowID = target?.nextWindowIDClockwise(on: screen) {
                let windowToFocusIndex = windows.firstIndex { $0.id() == nextWindowID } ?? 0
                return windows[windowToFocusIndex]
            } else {
                let windowIndex = windows.firstIndex(of: focusedWindow) ?? windows.count - 1
                let windowToFocusIndex = (windowIndex + 1) % windows.count
                return windows[windowToFocusIndex]
            }
        }()

        windowToFocus.focus()
    }

    func moveFocusToMain() {
        guard let focusedWindow = Window.currentlyFocused() else {
            focusScreen(at: 0)
            return
        }

        guard let screen = focusedWindow.screen() else {
            return
        }

        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {
            return
        }

        if focusedWindow.id() == windows[0].id() {
            (target?.lastFocusedWindow(on: screen) ?? windows[0]).focus()
        } else {
            windows[0].focus()
        }
    }

    func focusScreen(at screenIndex: Int) {
        guard let screen = target?.screen(at: screenIndex) else {
            return
        }

        // Do nothing if the screen is already focused
        if let focusedWindow = Window.currentlyFocused(), let focusedScreen = focusedWindow.screen(), focusedScreen == screen {
            return
        }

        // If the previous focus has been tracked, then focus the window that had the focus before.
        if let previouslyFocused = target?.lastFocusedWindow(on: screen), previouslyFocused.isOnScreen() {
            target?.executeTransition(.focusWindow(previouslyFocused))
            return
        }

        // If there are no windows on the screen focus the screen directly
        guard let windows = target?.windows(onScreen: screen), !windows.isEmpty else {
            target?.executeTransition(.focusScreen(screen))
            return
        }

        // Otherwise find the topmost window on the screen
        let screenCenter = NSPointToCGPoint(NSPoint(
            x: screen.frameIncludingDockAndMenu().midX,
            y: screen.frameIncludingDockAndMenu().midY
        ))

        // If there is no window at that point just focus the screen directly
        guard let topWindow = WindowsInformation.topWindowForScreenAtPoint(screenCenter, withWindows: windows) ?? windows.first else {
            target?.executeTransition(.focusScreen(screen))
            return
        }

        // Otherwise focus the topmost window
        target?.executeTransition(.focusWindow(topWindow))
    }

    func moveFocusScreenCounterClockwise() {
        guard let focusedScreen = Window.currentlyFocused()?.screen() else {
            return
        }

        guard let nextScreenIndex = target?.nextScreenIndexCounterClockwise(from: focusedScreen) else {
            return
        }

        focusScreen(at: nextScreenIndex)
    }

    func moveFocusScreenClockwise() {
        guard let focusedScreen = Window.currentlyFocused()?.screen() else {
            return
        }

        guard let screenIndex = target?.nextScreenIndexClockwise(from: focusedScreen) else {
            return
        }

        focusScreen(at: screenIndex)
    }

    func recentlyTriggeredFocusFollowsMouse() -> Bool {
        return focusFollowsMouseManager.recentlyTriggeredFocusFollowsMouse()
    }
}

extension FocusTransitionCoordinator: FocusFollowsMouseManagerDelegate {
    func windows(onScreen screen: Screen) -> [Window] {
        return target?.windows(onScreen: screen) ?? []
    }
}


================================================
FILE: Amethyst/Managers/HotKeyRegistrar.swift
================================================
//
//  HotKeyRegistrar.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/15/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import Foundation
import KeyboardShortcuts
import MASShortcut

protocol HotKeyRegistrar {
    func registerHotKey(with string: String?, modifiers: AMModifierFlags?, handler: @escaping () -> Void, defaultsKey: String, override: Bool)
}

extension HotKeyManager: HotKeyRegistrar {
    func registerHotKey(with string: String?, modifiers: AMModifierFlags?, handler: @escaping () -> Void, defaultsKey: String, override: Bool) {
        let name = KeyboardShortcuts.Name(defaultsKey)
        let migrationKey = "migrated-\(name.rawValue)"
        let isMigrated = UserDefaults.standard.bool(forKey: migrationKey)

        defer {
            UserDefaults.standard.set(true, forKey: migrationKey)
            KeyboardShortcuts.onKeyUp(for: name, action: handler)
        }

        if override {
            MASShortcutBinder.shared().breakBinding(withDefaultsKey: defaultsKey)
            UserDefaults.standard.removeObject(forKey: defaultsKey)
            KeyboardShortcuts.setShortcut(nil, for: name)
        }

        guard KeyboardShortcuts.getShortcut(for: name) == nil && (!isMigrated || override) else {
            return
        }

        if let value = UserDefaults.standard.object(forKey: defaultsKey),
           let shortcut = ValueTransformer(forName: .keyedUnarchiveFromDataTransformerName)?.transformedValue(value) as? MASShortcut {
            let shortcutKey = KeyboardShortcuts.Key(rawValue: shortcut.keyCode)
            let newShortcut = KeyboardShortcuts.Shortcut(shortcutKey, modifiers: shortcut.modifierFlags)
            // Keeping the old shortcuts in defaults for now to prevent data loss
//            UserDefaults.standard.removeObject(forKey: defaultsKey)
            KeyboardShortcuts.setShortcut(newShortcut, for: name)
            return
        }

        if let string = string, let modifiers = modifiers {
            if let keyCodes = stringToKeyCodes[string.lowercased()], !keyCodes.isEmpty {
                let shortcutKey = KeyboardShortcuts.Key(rawValue: keyCodes[0])
                let shortcut = KeyboardShortcuts.Shortcut(shortcutKey, modifiers: modifiers)
                KeyboardShortcuts.setShortcut(shortcut, for: name)
            } else {
                log.warning("String \"\(string)\" does not map to any keycodes")
            }
        }
    }
}


================================================
FILE: Amethyst/Managers/LayoutType.swift
================================================
//
//  LayoutManager.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/15/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import Foundation

extension FileManager {
    func layoutsDirectory() throws -> URL {
        let applicationSupportDirectory = try FileManager.default.url(
            for: .applicationSupportDirectory,
            in: .userDomainMask,
            appropriateFor: nil,
            create: true
        )
        let layoutsDirectory = applicationSupportDirectory
            .appendingPathComponent("Amethyst", isDirectory: true)
            .appendingPathComponent("Layouts", isDirectory: true)

        if !FileManager.default.fileExists(atPath: layoutsDirectory.path, isDirectory: nil) {
            try FileManager.default.createDirectory(
                at: layoutsDirectory,
                withIntermediateDirectories: true,
                attributes: nil
            )
        }

        return layoutsDirectory
    }

    func layoutFile(key: String) throws -> URL {
        let layoutsDirectory = try self.layoutsDirectory()
        return layoutsDirectory.appendingPathComponent("\(key).js")
    }
}

enum LayoutType<Window: WindowType> {
    enum Error: Swift.Error {
        case unknownLayout
    }

    case tall
    case tallRight
    case wide
    case twoPane
    case twoPaneRight
    case threeColumnLeft
    case threeColumnMiddle
    case threeColumnRight
    case fourColumnLeft
    case fourColumnRight
    case fullscreen
    case column
    case row
    case floating
    case widescreenTallLeft
    case widescreenTallRight
    case binarySpacePartitioning

    case custom(key: String)

    static var standardLayouts: [LayoutType<Window>] {
        return [
            .tall,
            .tallRight,
            .wide,
            .twoPane,
            .twoPaneRight,
            .threeColumnLeft,
            .threeColumnMiddle,
            .threeColumnRight,
            .fourColumnLeft,
            .fourColumnRight,
            .fullscreen,
            .column,
            .row,
            .floating,
            .widescreenTallLeft,
            .widescreenTallRight,
            .binarySpacePartitioning
        ]
    }

    var key: String {
        switch self {
        case .tall:
            return "tall"
        case .tallRight:
            return "tall-right"
        case .wide:
            return "wide"
        case .twoPane:
            return "two-pane"
        case .twoPaneRight:
            return "two-pane-right"
        case .threeColumnLeft:
            return "3column-left"
        case .threeColumnMiddle:
            return "middle-wide"
        case .threeColumnRight:
            return "3column-right"
        case .fourColumnLeft:
            return "4column-left"
        case .fourColumnRight:
            return "4column-right"
        case .fullscreen:
            return "fullscreen"
        case .column:
            return "column"
        case .row:
            return "row"
        case .floating:
            return "floating"
        case .widescreenTallLeft:
            return "widescreen-tall"
        case .widescreenTallRight:
            return "widescreen-tall-right"
        case .binarySpacePartitioning:
            return "bsp"
        case .custom(let key):
            return key
        }
    }

    var layoutClass: Layout<Window>.Type {
        switch self {
        case .tall:
            return TallLayout<Window>.self
        case .tallRight:
            return TallRightLayout<Window>.self
        case .wide:
            return WideLayout<Window>.self
        case .twoPane:
            return TwoPaneLayout<Window>.self
        case .twoPaneRight:
            return TwoPaneRightLayout<Window>.self
        case .threeColumnLeft:
            return ThreeColumnLeftLayout<Window>.self
        case .threeColumnMiddle:
            return ThreeColumnMiddleLayout<Window>.self
        case .threeColumnRight:
            return ThreeColumnRightLayout<Window>.self
        case .fourColumnLeft:
            return FourColumnLeftLayout<Window>.self
        case .fourColumnRight:
            return FourColumnRightLayout<Window>.self
        case .fullscreen:
            return FullscreenLayout<Window>.self
        case .column:
            return ColumnLayout<Window>.self
        case .row:
            return RowLayout<Window>.self
        case .floating:
            return FloatingLayout<Window>.self
        case .widescreenTallLeft:
            return WidescreenTallLayoutLeft<Window>.self
        case .widescreenTallRight:
            return WidescreenTallLayoutRight<Window>.self
        case .binarySpacePartitioning:
            return BinarySpacePartitioningLayout<Window>.self
        case .custom:
            return CustomLayout<Window>.self
        }
    }

    static func from(key: String) -> LayoutType<Window> {
        switch key {
        case "tall":
            return .tall
        case "tall-right":
            return .tallRight
        case "wide":
            return .wide
        case "two-pane":
            return .twoPane
        case "two-pane-right":
            return .twoPaneRight
        case "3column-left":
            return .threeColumnLeft
        case "middle-wide":
            return .threeColumnMiddle
        case "3column-right":
            return .threeColumnRight
        case "4column-left":
            return .fourColumnLeft
        case "4column-right":
            return .fourColumnRight
        case "fullscreen":
            return .fullscreen
        case "column":
            return .column
        case "row":
            return .row
        case "floating":
            return .floating
        case "widescreen-tall":
            return .widescreenTallLeft
        case "widescreen-tall-right":
            return .widescreenTallRight
        case "bsp":
            return .binarySpacePartitioning
        default:
            return .custom(key: key)
        }
    }

    static func layoutForKey(_ layoutKey: String) -> Layout<Window>? {
        let type = LayoutType<Window>.from(key: layoutKey)

        guard case .custom = type else {
            return type.layoutClass.init()
        }

        do {
            let layoutFile = try FileManager.default.layoutFile(key: layoutKey)

            guard FileManager.default.fileExists(atPath: layoutFile.path) else {
                return nil
            }

            return CustomLayout<Window>(key: layoutKey, fileURL: layoutFile)
        } catch {
            return nil
        }
    }

    static func layoutNameForKey(_ layoutKey: String) -> String? {
        let type = LayoutType<Window>.from(key: layoutKey)

        guard case .custom = type else {
            return type.layoutClass.layoutName
        }

        return layoutForKey(layoutKey)?.layoutName
    }

    static func standardLayoutClasses() -> [Layout<Window>.Type] {
        return standardLayouts.compactMap { $0.layoutClass }
    }

    // Returns a list of (key, name) pairs
    static func availableLayoutStrings() -> [(key: String, name: String)] {
        var layoutTypes = standardLayouts
            .compactMap { $0.layoutClass }
            .map { ($0.layoutKey, $0.layoutName) }

        do {
            let layoutsDirectory = try FileManager.default.layoutsDirectory()
            let customLayoutFiles = try FileManager.default.contentsOfDirectory(
                at: layoutsDirectory,
                includingPropertiesForKeys: nil,
                options: []
            ).filter { $0.pathExtension == "js" }

            let customLayouts = customLayoutFiles
                .map { $0.deletingPathExtension().lastPathComponent }
                .map { ($0, layoutNameForKey($0)!) }
            layoutTypes.append(contentsOf: customLayouts)
        } catch {
            log.error("failed to parse custom layouts")
        }

        return layoutTypes
    }

    static func layoutsWithConfiguration(_ userConfiguration: UserConfiguration) -> [Layout<Window>] {
        let layoutKeys: [String] = userConfiguration.layoutKeys()
        let layouts = layoutKeys.map { layoutKey -> Layout<Window>? in
            guard let layout = LayoutType.layoutForKey(layoutKey) else {
                log.warning("Unrecognized layout key \(layoutKey)")
                return nil
            }

            return layout
        }

        return layouts.compactMap { $0 }
    }

    static func encoded(layout: Layout<Window>) throws -> Data {
        return try JSONEncoder().encode(layout)
    }

    static func decoded(data: Data, key: String) throws -> Layout<Window> {
        let layoutType = LayoutType<Window>.from(key: key)
        let decoder = JSONDecoder()
        return try decoder.decode(layoutType.layoutClass, from: data)
    }
}


================================================
FILE: Amethyst/Managers/LogManager.swift
================================================
//
//  LogManager.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 5/19/16.
//  Copyright © 2016 Ian Ynda-Hummel. All rights reserved.
//

import SwiftyBeaver

let log = SwiftyBeaver.self


================================================
FILE: Amethyst/Managers/ScreenManager.swift
================================================
//
//  ScreenManager.swift
//  Amethyst
//
//  Created by Ian Ynda-Hummel on 12/23/15.
//  Copyright © 2015 Ian Ynda-Hummel. All rights reserved.
//

import Foundation
import Silica

/// Information about a layout for display in menus
struct LayoutMenuItemInfo {
    let key: String
    let name: String
    let isSelected: Bool
}

protocol ScreenManagerDelegate: AnyObject {
    associatedtype Window: WindowType
    func applyWindowLimit(forScreenManager screenManager: ScreenManager<Self>, minimizingIn range: (_ windowCount: Int) -> Range<Int>)
    func activeWindowSet(forScreenManager screenManager: ScreenManager<Self>) -> WindowSet<Window>
    func onReflowInitiation()
    func onReflowCompletion()
}

final class ScreenManager<Delegate: ScreenManagerDelegate>: NSObject, Codable {
    typealias Window = Delegate.Window
    typealias Screen = Window.Screen

    enum CodingKeys: String, CodingKey {
        case layoutsBySpaceUUID
    }

    weak var delegate: Delegate?

    private(set) var screen: Screen?
    private(set) var space: Space?

    /// The last window that has been focused on the screen. This value is updated by the notification observations in
    /// `ObserveApplicationNotifications`.
    private(set) var lastFocusedWindow: Window?
    private let userConfiguration: UserConfiguration

    private let reflowOperationDispatchQueue = DispatchQueue(
        label: "ScreenManager.reflowOperationQueue",
        qos: .utility,
        attributes: [],
        autoreleaseFrequency: .inherit,
        target: nil
    )
    private let reflowOperationQueue = OperationQueue()

    private var layouts: [Layout<Window>] = []
    private var currentLayoutIndexBySpaceUUID: [String: Int] = [:]
    private var layoutsBySpaceUUID: [String: [Layout<Window>]] = [:]
    private var currentLayoutIndex: Int = 0
    var previousLayoutKey: String?
    var currentLayout: Layout<Window>? {
        guard !layouts.isEmpty else {
            return nil
        }
        return layouts[currentLayoutIndex]
    }

    /// Returns layout info for all layouts in this screen manager, including selection state
    var layoutsInfo: [LayoutMenuItemInfo] {
        return layouts.enumerated().map { index, layout in
            LayoutMenuItemInfo(
                key: layout.layoutKey,
                name: layout.layoutName,
                isSelected: index == currentLayoutIndex
            )
        }
    }

    private let layoutNameWindowController: LayoutNameWindowController

    init(screen: Screen, delegate: Delegate, userConfiguration: UserConfiguration) {
        self.screen = screen
        self.delegate = delegate
        self.userConfiguration = userConfiguration

        layoutNameWindowController = LayoutNameWindowController(windowNibName: "LayoutNameWindow")

        super.init()

        layouts = LayoutType.layoutsWithConfiguration(userConfiguration)

        reflowOperationQueue.underlyingQueue = reflowOperationDispatchQueue
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let layoutsBySpaceUUID = try values.decode([String: [[String: Data]]].self, forKey: .layoutsBySpaceUUID)

        self.userConfiguration = UserConfiguration.shared
        self.layoutsBySpaceUUID = try layoutsBySpaceUUID.mapValues { keyedLayouts -> [Layout<Window>] in
            return try ScreenManager<Delegate>.decodedLayouts(from: keyedLayouts, userConfiguration: UserConfiguration.shared)
        }

        layoutNameWindowController = LayoutNameWindowController(windowNibName: "LayoutNameWindow")
    }

    /**
     Takes the list of layouts and inserts decoded layouts where appropriate.

     - Parameters:
        - encodedLayouts: A list of encoded layouts to be restored.
        - userConfiguration: User configuration defining the list of layouts.
     */
    static func decodedLayouts(from encodedLayouts: [[String: Data]], userConfiguration: UserConfiguration) throws -> [Layout<Window>] {
        let layouts: [Layout<Window>] = LayoutType.layoutsWithConfiguration(userConfiguration)
    
Download .txt
gitextract_92so_qqv/

├── .amethyst.sample.yml
├── .eslintrc.yml
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows/
│       └── tests.yml
├── .gitignore
├── .hound.yml
├── .swiftlint.yml
├── Amethyst/
│   ├── Amethyst-Bridging-Header.h
│   ├── Amethyst-Info.plist
│   ├── Amethyst.entitlements
│   ├── AmethystDebug.entitlements
│   ├── AppDelegate.swift
│   ├── Base.lproj/
│   │   └── MainMenu.xib
│   ├── Categories/
│   │   ├── NSRunningApplication+Manageable.swift
│   │   └── NSTableView+Amethyst.swift
│   ├── Debug/
│   │   ├── AppsInfo.swift
│   │   ├── DebugInfo.swift
│   │   ├── ScreensInfo.swift
│   │   └── WindowsInfo.swift
│   ├── Events/
│   │   └── HotKeyManager.swift
│   ├── Images.xcassets/
│   │   ├── 123.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   ├── amethyst.imageset/
│   │   │   └── Contents.json
│   │   ├── dots.and.line.vertical.and.cursorarrow.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-statusitem-disabled.imageset/
│   │   │   └── Contents.json
│   │   ├── icon-statusitem.imageset/
│   │   │   └── Contents.json
│   │   ├── macwindow.on.rectangle.imageset/
│   │   │   └── Contents.json
│   │   ├── text.and.command.macwindow.imageset/
│   │   │   └── Contents.json
│   │   ├── uiwindow.split.2x1.imageset/
│   │   │   └── Contents.json
│   │   └── waveform.path.ecg.rectangle.imageset/
│   │       └── Contents.json
│   ├── Layout/
│   │   ├── Layout.swift
│   │   ├── Layouts/
│   │   │   ├── BinarySpacePartitioningLayout.swift
│   │   │   ├── ColumnLayout.swift
│   │   │   ├── CustomLayout.swift
│   │   │   ├── FloatingLayout.swift
│   │   │   ├── FourColumnLayout.swift
│   │   │   ├── FullscreenLayout.swift
│   │   │   ├── RowLayout.swift
│   │   │   ├── TallLayout.swift
│   │   │   ├── TallRightLayout.swift
│   │   │   ├── ThreeColumnLayout.swift
│   │   │   ├── TwoPaneLayout.swift
│   │   │   ├── TwoPaneRightLayout.swift
│   │   │   ├── WideLayout.swift
│   │   │   └── WidescreenTallLayout.swift
│   │   └── ReflowOperation.swift
│   ├── Managers/
│   │   ├── AppManager.swift
│   │   ├── FocusFollowsMouseManager.swift
│   │   ├── FocusTransitionCoordinator.swift
│   │   ├── HotKeyRegistrar.swift
│   │   ├── LayoutType.swift
│   │   ├── LogManager.swift
│   │   ├── ScreenManager.swift
│   │   ├── Screens.swift
│   │   ├── WindowManager.swift
│   │   ├── WindowTransitionCoordinator.swift
│   │   └── Windows.swift
│   ├── Model/
│   │   ├── Application.swift
│   │   ├── ApplicationEventHandler.swift
│   │   ├── ApplicationObservation.swift
│   │   ├── CGInfo.swift
│   │   ├── Change.swift
│   │   ├── MouseState.swift
│   │   ├── Reliability.swift
│   │   ├── Screen.swift
│   │   ├── Space.swift
│   │   ├── Window.swift
│   │   └── WindowsInformation.swift
│   ├── Preferences/
│   │   ├── DebugPreferencesViewController.swift
│   │   ├── DebugPreferencesViewController.xib
│   │   ├── FloatingPreferencesViewController.swift
│   │   ├── FloatingPreferencesViewController.xib
│   │   ├── GeneralPreferencesViewController.swift
│   │   ├── GeneralPreferencesViewController.xib
│   │   ├── LayoutsPreferencesViewController.swift
│   │   ├── LayoutsPreferencesViewController.xib
│   │   ├── MousePreferencesViewController.swift
│   │   ├── MousePreferencesViewController.xib
│   │   ├── ShortcutsPreferencesListItemView.swift
│   │   ├── ShortcutsPreferencesViewController.swift
│   │   ├── ShortcutsPreferencesViewController.xib
│   │   └── UserConfiguration.swift
│   ├── View/
│   │   ├── LayoutNameWindow.swift
│   │   ├── LayoutNameWindow.xib
│   │   ├── LayoutNameWindowController.swift
│   │   └── PreferencesWindow.swift
│   ├── default.amethyst
│   ├── en.lproj/
│   │   ├── Credits.rtf
│   │   └── InfoPlist.strings
│   └── main.swift
├── Amethyst.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── swiftpm/
│   │           └── Package.resolved
│   └── xcshareddata/
│       └── xcschemes/
│           ├── Amethyst Debug CLI.xcscheme
│           └── Amethyst.xcscheme
├── Amethyst.xctestplan
├── Amethyst.xcworkspace/
│   ├── contents.xcworkspacedata
│   └── xcshareddata/
│       ├── IDEWorkspaceChecks.plist
│       └── swiftpm/
│           └── Package.resolved
├── AmethystTests/
│   ├── AmethystTests-Bridging-Header.h
│   ├── Helpers/
│   │   ├── FrameAssignmentVerification.swift
│   │   └── TestBundle.swift
│   ├── Info.plist
│   ├── Model/
│   │   ├── CustomLayouts/
│   │   │   ├── extended.js
│   │   │   ├── fullscreen.js
│   │   │   ├── null.js
│   │   │   ├── recommended-main-pane-ratio.js
│   │   │   ├── static-ratio-tall-native-commands.js
│   │   │   ├── static-ratio-tall.js
│   │   │   ├── subset.js
│   │   │   ├── undefined.js
│   │   │   └── uniform-columns.js
│   │   ├── TestScreen.swift
│   │   └── TestWindow.swift
│   └── Tests/
│       ├── Categories/
│       │   └── SIWindow+AmethystTests.swift
│       ├── Configuration/
│       │   └── UserConfigurationTests.swift
│       ├── Layout/
│       │   ├── BinarySpacePartitioningLayoutTests.swift
│       │   ├── ColumnLayoutTests.swift
│       │   ├── CustomLayoutTests.swift
│       │   ├── FloatingLayoutTests.swift
│       │   ├── FullscreenLayoutTests.swift
│       │   ├── RowLayoutTests.swift
│       │   ├── TallLayoutTests.swift
│       │   ├── TallRightLayoutTests.swift
│       │   ├── ThreeColumnLayoutTests.swift
│       │   ├── TwoPaneLayoutTests.swift
│       │   ├── WideLayoutTests.swift
│       │   └── WidescreenTallLayoutTests.swift
│       └── Managers/
│           ├── HotKeyManagerTests.swift
│           └── ScreenManagerTests.swift
├── Brewfile
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── docs/
│   ├── configuration-files.md
│   ├── custom-layouts.md
│   ├── troubleshooting.md
│   └── window-limit.md
├── exportOptions.plist
├── fastlane/
│   ├── Appfile
│   ├── Fastfile
│   ├── Gymfile
│   └── README.md
└── privacy-policy.md
Download .txt
SYMBOL INDEX (9 symbols across 9 files)

FILE: AmethystTests/Model/CustomLayouts/extended.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/fullscreen.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/null.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/recommended-main-pane-ratio.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/static-ratio-tall-native-commands.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/static-ratio-tall.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/subset.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/undefined.js
  function layout (line 1) | function layout() {

FILE: AmethystTests/Model/CustomLayouts/uniform-columns.js
  function layout (line 1) | function layout() {
Condensed preview — 146 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (973K chars).
[
  {
    "path": ".amethyst.sample.yml",
    "chars": 9899,
    "preview": "# Default settings for Amethyst\n# Repo: `https://github.com/ianyh/Amethyst`\n#\n# Note due to issue 1419 (https://github.c"
  },
  {
    "path": ".eslintrc.yml",
    "chars": 73,
    "preview": "---\n    env:\n        es6: true\n    parserOptions:\n        ecmaVersion: 9\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 773,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 595,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
  },
  {
    "path": ".github/workflows/tests.yml",
    "chars": 429,
    "preview": "name: Tests\n\non:\n  push:\n    branches: [ development ]\n  pull_request:\n\njobs:\n  build:\n    name: Build and run unit test"
  },
  {
    "path": ".gitignore",
    "chars": 337,
    "preview": "# Xcode\n.DS_Store\n*/build/*\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspecti"
  },
  {
    "path": ".hound.yml",
    "chars": 101,
    "preview": "swiftlint:\n    config_file: .swiftlint.yml\n\neslint:\n    enabled: true\n    config_file: .eslintrc.yml\n"
  },
  {
    "path": ".swiftlint.yml",
    "chars": 406,
    "preview": "disabled_rules:\n    - function_body_length\n    - closing_brace\n    - statement_position\n    - force_cast\n    - force_try"
  },
  {
    "path": "Amethyst/Amethyst-Bridging-Header.h",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "Amethyst/Amethyst-Info.plist",
    "chars": 1260,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Amethyst/Amethyst.entitlements",
    "chars": 181,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Amethyst/AmethystDebug.entitlements",
    "chars": 311,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Amethyst/AppDelegate.swift",
    "chars": 10205,
    "preview": "//\n//  AppDelegate.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2016 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Base.lproj/MainMenu.xib",
    "chars": 12039,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Categories/NSRunningApplication+Manageable.swift",
    "chars": 1883,
    "preview": "//\n//  NSRunningApplication+Manageable.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2"
  },
  {
    "path": "Amethyst/Categories/NSTableView+Amethyst.swift",
    "chars": 233,
    "preview": "//\n//  NSTableView+Amethyst.swift\n//  Amethyst\n//\n//  Created by James Zaghini on 15/5/18.\n//  Copyright © 2018 Ian Ynda"
  },
  {
    "path": "Amethyst/Debug/AppsInfo.swift",
    "chars": 914,
    "preview": "//\n//  AppsInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/23.\n//  Copyright © 2023 Ian Ynda-Hummel. Al"
  },
  {
    "path": "Amethyst/Debug/DebugInfo.swift",
    "chars": 2159,
    "preview": "//\n//  DebugInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/24/20.\n//  Copyright © 2020 Ian Ynda-Hummel. "
  },
  {
    "path": "Amethyst/Debug/ScreensInfo.swift",
    "chars": 442,
    "preview": "//\n//  ScreensInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/23.\n//  Copyright © 2023 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Debug/WindowsInfo.swift",
    "chars": 1580,
    "preview": "//\n//  WindowsInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/5/23.\n//  Copyright © 2023 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Events/HotKeyManager.swift",
    "chars": 19798,
    "preview": "//\n//  HotKeyManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Humm"
  },
  {
    "path": "Amethyst/Images.xcassets/123.rectangle.imageset/Contents.json",
    "chars": 163,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"123.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \""
  },
  {
    "path": "Amethyst/Images.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1301,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : "
  },
  {
    "path": "Amethyst/Images.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Amethyst/Images.xcassets/amethyst.imageset/Contents.json",
    "chars": 273,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    {\n   "
  },
  {
    "path": "Amethyst/Images.xcassets/dots.and.line.vertical.and.cursorarrow.rectangle.imageset/Contents.json",
    "chars": 198,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"dots.and.line.vertical.and.cursorarrow.rectangle.svg\",\n      \"idiom\" : \"unive"
  },
  {
    "path": "Amethyst/Images.xcassets/icon-statusitem-disabled.imageset/Contents.json",
    "chars": 301,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-statusitem-disabled.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n  "
  },
  {
    "path": "Amethyst/Images.xcassets/icon-statusitem.imageset/Contents.json",
    "chars": 283,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-statusitem.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    "
  },
  {
    "path": "Amethyst/Images.xcassets/macwindow.on.rectangle.imageset/Contents.json",
    "chars": 172,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"macwindow.on.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" "
  },
  {
    "path": "Amethyst/Images.xcassets/text.and.command.macwindow.imageset/Contents.json",
    "chars": 176,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"text.and.command.macwindow.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"in"
  },
  {
    "path": "Amethyst/Images.xcassets/uiwindow.split.2x1.imageset/Contents.json",
    "chars": 168,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"uiwindow.split.2x1.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n"
  },
  {
    "path": "Amethyst/Images.xcassets/waveform.path.ecg.rectangle.imageset/Contents.json",
    "chars": 177,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"waveform.path.ecg.rectangle.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"i"
  },
  {
    "path": "Amethyst/Layout/Layout.swift",
    "chars": 8403,
    "preview": "//\n//  Layout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/3/15.\n//  Copyright © 2015 Ian Ynda-Hummel. All"
  },
  {
    "path": "Amethyst/Layout/Layouts/BinarySpacePartitioningLayout.swift",
    "chars": 13281,
    "preview": "//\n//  BinarySpacePartitioningLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/29/16.\n//  Copyright © 20"
  },
  {
    "path": "Amethyst/Layout/Layouts/ColumnLayout.swift",
    "chars": 3960,
    "preview": "//\n//  ColumnLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Humm"
  },
  {
    "path": "Amethyst/Layout/Layouts/CustomLayout.swift",
    "chars": 13887,
    "preview": "//\n//  CustomLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 7/2/21.\n//  Copyright © 2021 Ian Ynda-Hummel"
  },
  {
    "path": "Amethyst/Layout/Layouts/FloatingLayout.swift",
    "chars": 567,
    "preview": "//\n//  FloatingLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hu"
  },
  {
    "path": "Amethyst/Layout/Layouts/FourColumnLayout.swift",
    "chars": 10637,
    "preview": "//\n//  FourColumnLayout.swift\n//  Amethyst\n//\n//  Originally created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 201"
  },
  {
    "path": "Amethyst/Layout/Layouts/FullscreenLayout.swift",
    "chars": 1249,
    "preview": "//\n//  FullscreenLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-"
  },
  {
    "path": "Amethyst/Layout/Layouts/RowLayout.swift",
    "chars": 3974,
    "preview": "//\n//  RowLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Layout/Layouts/TallLayout.swift",
    "chars": 3997,
    "preview": "//\n//  TallLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel"
  },
  {
    "path": "Amethyst/Layout/Layouts/TallRightLayout.swift",
    "chars": 4044,
    "preview": "//\n//  TallRightLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-H"
  },
  {
    "path": "Amethyst/Layout/Layouts/ThreeColumnLayout.swift",
    "chars": 9837,
    "preview": "//\n//  ThreeColumnLayout.swift\n//  Amethyst\n//\n//  Originally created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 20"
  },
  {
    "path": "Amethyst/Layout/Layouts/TwoPaneLayout.swift",
    "chars": 4007,
    "preview": "//\n//  TwoPaneLayout.swift\n//  Amethyst\n//\n//  Created by @mwz on 10/06/2021.\n//  Copyright © 2021 Ian Ynda-Hummel. All "
  },
  {
    "path": "Amethyst/Layout/Layouts/TwoPaneRightLayout.swift",
    "chars": 4032,
    "preview": "//\n//  TwoPaneRightLayout.swift\n//  Amethyst\n//\n//  Created by Anja on 16.06.23.\n//  Copyright © 2023 Ian Ynda-Hummel. A"
  },
  {
    "path": "Amethyst/Layout/Layouts/WideLayout.swift",
    "chars": 3943,
    "preview": "//\n//  WideLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/14/15.\n//  Copyright © 2015 Ian Ynda-Hummel"
  },
  {
    "path": "Amethyst/Layout/Layouts/WidescreenTallLayout.swift",
    "chars": 4921,
    "preview": "//\n//  WidescreenTallLayout.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/15/15.\n//  Copyright © 2015 Ian Y"
  },
  {
    "path": "Amethyst/Layout/ReflowOperation.swift",
    "chars": 8335,
    "preview": "//\n//  ReflowOperation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/19/19.\n//  Copyright © 2019 Ian Ynda-Hu"
  },
  {
    "path": "Amethyst/Managers/AppManager.swift",
    "chars": 656,
    "preview": "//\n//  relaunch.swift\n//  Amethyst\n//\n//  Created by Agustin Suarez on 2021-02-23.\n//  Copyright © 2021 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Managers/FocusFollowsMouseManager.swift",
    "chars": 2998,
    "preview": "//\n//  FocusFollowsMouseManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ia"
  },
  {
    "path": "Amethyst/Managers/FocusTransitionCoordinator.swift",
    "chars": 6626,
    "preview": "//\n//  FocusTransitionCoordinator.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/24/19.\n//  Copyright © 2019 "
  },
  {
    "path": "Amethyst/Managers/HotKeyRegistrar.swift",
    "chars": 2452,
    "preview": "//\n//  HotKeyRegistrar.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Hu"
  },
  {
    "path": "Amethyst/Managers/LayoutType.swift",
    "chars": 8804,
    "preview": "//\n//  LayoutManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-Humm"
  },
  {
    "path": "Amethyst/Managers/LogManager.swift",
    "chars": 195,
    "preview": "//\n//  LogManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/19/16.\n//  Copyright © 2016 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Managers/ScreenManager.swift",
    "chars": 15293,
    "preview": "//\n//  ScreenManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 12/23/15.\n//  Copyright © 2015 Ian Ynda-Hum"
  },
  {
    "path": "Amethyst/Managers/Screens.swift",
    "chars": 4310,
    "preview": "//\n//  Screens.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. Al"
  },
  {
    "path": "Amethyst/Managers/WindowManager.swift",
    "chars": 42605,
    "preview": "//\n//  WindowManager.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/14/16.\n//  Copyright © 2016 Ian Ynda-Humm"
  },
  {
    "path": "Amethyst/Managers/WindowTransitionCoordinator.swift",
    "chars": 7078,
    "preview": "//\n//  WindowTransitionCoordinator.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/24/19.\n//  Copyright © 2019"
  },
  {
    "path": "Amethyst/Managers/Windows.swift",
    "chars": 8271,
    "preview": "//\n//  Windows.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/15/19.\n//  Copyright © 2019 Ian Ynda-Hummel. Al"
  },
  {
    "path": "Amethyst/Model/Application.swift",
    "chars": 7421,
    "preview": "//\n//  Application.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/12/19.\n//  Copyright © 2019 Ian Ynda-Hummel"
  },
  {
    "path": "Amethyst/Model/ApplicationEventHandler.swift",
    "chars": 3120,
    "preview": "//\n//  ApplicationEventHandler.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/28/23.\n//  Copyright © 2023 Ian"
  },
  {
    "path": "Amethyst/Model/ApplicationObservation.swift",
    "chars": 12506,
    "preview": "//\n//  ApplicationObservation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/21/19.\n//  Copyright © 2019 Ian "
  },
  {
    "path": "Amethyst/Model/CGInfo.swift",
    "chars": 6172,
    "preview": "//\n//  CGInfo.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All"
  },
  {
    "path": "Amethyst/Model/Change.swift",
    "chars": 546,
    "preview": "//\n//  Change.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/29/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All"
  },
  {
    "path": "Amethyst/Model/MouseState.swift",
    "chars": 5007,
    "preview": "//\n//  MouseState.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/21/19.\n//  Copyright © 2019 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Model/Reliability.swift",
    "chars": 568,
    "preview": "//\n//  Reliability.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/8/19.\n//  Copyright © 2019 Ian Ynda-Hummel."
  },
  {
    "path": "Amethyst/Model/Screen.swift",
    "chars": 6308,
    "preview": "//\n//  Screen.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All"
  },
  {
    "path": "Amethyst/Model/Space.swift",
    "chars": 272,
    "preview": "//\n//  Space.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 9/9/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All r"
  },
  {
    "path": "Amethyst/Model/Window.swift",
    "chars": 12239,
    "preview": "//\n//  Window.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/10/19.\n//  Copyright © 2019 Ian Ynda-Hummel. All"
  },
  {
    "path": "Amethyst/Model/WindowsInformation.swift",
    "chars": 6715,
    "preview": "//\n//  WindowsInformation.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda"
  },
  {
    "path": "Amethyst/Preferences/DebugPreferencesViewController.swift",
    "chars": 256,
    "preview": "//\n//  DebugPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/9/19.\n//  Copyright © 20"
  },
  {
    "path": "Amethyst/Preferences/DebugPreferencesViewController.xib",
    "chars": 7055,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/FloatingPreferencesViewController.swift",
    "chars": 8837,
    "preview": "//\n//  GeneralPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright ©"
  },
  {
    "path": "Amethyst/Preferences/FloatingPreferencesViewController.xib",
    "chars": 45283,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/GeneralPreferencesViewController.swift",
    "chars": 274,
    "preview": "//\n//  GeneralPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright ©"
  },
  {
    "path": "Amethyst/Preferences/GeneralPreferencesViewController.xib",
    "chars": 40924,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/LayoutsPreferencesViewController.swift",
    "chars": 5022,
    "preview": "//\n//  LayoutsPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 3/1/20.\n//  Copyright © "
  },
  {
    "path": "Amethyst/Preferences/LayoutsPreferencesViewController.xib",
    "chars": 21419,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/MousePreferencesViewController.swift",
    "chars": 238,
    "preview": "//\n//  MousePreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/29/20.\n//  Copyright © 2"
  },
  {
    "path": "Amethyst/Preferences/MousePreferencesViewController.xib",
    "chars": 6986,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesListItemView.swift",
    "chars": 1277,
    "preview": "//\n//  ShortcutsPreferencesListItemView.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright ©"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesViewController.swift",
    "chars": 1506,
    "preview": "//\n//  ShortcutsPreferencesViewController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright"
  },
  {
    "path": "Amethyst/Preferences/ShortcutsPreferencesViewController.xib",
    "chars": 9937,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/Preferences/UserConfiguration.swift",
    "chars": 27438,
    "preview": "//\n//  UserConfiguration.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/8/16.\n//  Copyright © 2016 Ian Ynda-H"
  },
  {
    "path": "Amethyst/View/LayoutNameWindow.swift",
    "chars": 2578,
    "preview": "//\n//  LayoutNameWindow.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian Ynda-H"
  },
  {
    "path": "Amethyst/View/LayoutNameWindow.xib",
    "chars": 5984,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "Amethyst/View/LayoutNameWindowController.swift",
    "chars": 235,
    "preview": "//\n//  LayoutNameWIndowController.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 1/16/16.\n//  Copyright © 2016 "
  },
  {
    "path": "Amethyst/View/PreferencesWindow.swift",
    "chars": 1786,
    "preview": "//\n//  PreferencesWindow.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 7/19/17.\n//  Copyright © 2017 Ian Ynda-"
  },
  {
    "path": "Amethyst/default.amethyst",
    "chars": 4630,
    "preview": "{\n    \"layouts\": [\n        \"tall\",\n        \"wide\",\n        \"fullscreen\",\n        \"column\"\n    ],\n    \"mod1\": [\n        \""
  },
  {
    "path": "Amethyst/en.lproj/Credits.rtf",
    "chars": 436,
    "preview": "{\\rtf0\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\n{\\colortbl;\\red255\\green255\\blue255;}\n\\paperw9840\\paperh8400\n\\pard\\tx560\\tx11"
  },
  {
    "path": "Amethyst/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "Amethyst/main.swift",
    "chars": 1118,
    "preview": "//\n//  main.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 2/24/20.\n//  Copyright © 2020 Ian Ynda-Hummel. All r"
  },
  {
    "path": "Amethyst.xcodeproj/project.pbxproj",
    "chars": 87395,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Amethyst.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 153,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:Amethyst.xcodep"
  },
  {
    "path": "Amethyst.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "chars": 3892,
    "preview": "{\n  \"originHash\" : \"0bdfbd20a0602e0ca7fe7531ef1f5ef5df49b4edc989a6121656a1209da2ca9e\",\n  \"pins\" : [\n    {\n      \"identit"
  },
  {
    "path": "Amethyst.xcodeproj/xcshareddata/xcschemes/Amethyst Debug CLI.xcscheme",
    "chars": 5425,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1500\"\n   version = \"2.0\">\n   <BuildAction\n      "
  },
  {
    "path": "Amethyst.xcodeproj/xcshareddata/xcschemes/Amethyst.xcscheme",
    "chars": 4705,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1500\"\n   version = \"1.7\">\n   <BuildAction\n      "
  },
  {
    "path": "Amethyst.xctestplan",
    "chars": 683,
    "preview": "{\n  \"configurations\" : [\n    {\n      \"id\" : \"3A3BA42F-5862-48E3-9505-9D3676A1BA59\",\n      \"name\" : \"Standard\",\n      \"op"
  },
  {
    "path": "Amethyst.xcworkspace/contents.xcworkspacedata",
    "chars": 226,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Amethyst.xcode"
  },
  {
    "path": "Amethyst.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Amethyst.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "chars": 4145,
    "preview": "{\n  \"originHash\" : \"5c090c150afce6ddb786afef9aa71f0237da58318f433cf9eea579c374d84d51\",\n  \"pins\" : [\n    {\n      \"identit"
  },
  {
    "path": "AmethystTests/AmethystTests-Bridging-Header.h",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "AmethystTests/Helpers/FrameAssignmentVerification.swift",
    "chars": 2313,
    "preview": "//\n//  FrameAssignmentVerification.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright ©"
  },
  {
    "path": "AmethystTests/Helpers/TestBundle.swift",
    "chars": 433,
    "preview": "//\n//  TestBundle.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 7/8/21.\n//  Copyright © 2021 Ian Ynda-Hum"
  },
  {
    "path": "AmethystTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/extended.js",
    "chars": 766,
    "preview": "function layout() {\n    return {\n        name: \"Extended Tall\",\n        extends: \"tall\",\n        getFrameAssignments: (w"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/fullscreen.js",
    "chars": 229,
    "preview": "function layout() {\n    return {\n        name: \"Fullscreen\",\n        getFrameAssignments: (windows, screenFrame) => {\n  "
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/null.js",
    "chars": 98,
    "preview": "function layout() {\n    return {\n        name: \"Null\",\n        getFrameAssignments: null\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/recommended-main-pane-ratio.js",
    "chars": 1403,
    "preview": "function layout() {\n    return {\n        name: \"Ratio\",\n        initialState: {\n            mainPaneRatio: 0.5\n        }"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/static-ratio-tall-native-commands.js",
    "chars": 2085,
    "preview": "function layout() {\n    return {\n        name: \"Static Ratio Tall with Native Commands\",\n        initialState: {\n       "
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/static-ratio-tall.js",
    "chars": 2056,
    "preview": "function layout() {\n    return {\n        name: \"Static Ratio Tall\",\n        initialState: {\n            mainPaneCount: 1"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/subset.js",
    "chars": 3622,
    "preview": "function layout() {\n    return {\n        name: \"Subset\",\n        initialState: {\n            ids: []\n        },\n        "
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/undefined.js",
    "chars": 68,
    "preview": "function layout() {\n    return {\n        name: \"Undefined\"\n    };\n}\n"
  },
  {
    "path": "AmethystTests/Model/CustomLayouts/uniform-columns.js",
    "chars": 655,
    "preview": "function layout() {\n    return {\n        name: \"Uniform Columns\",\n        getFrameAssignments: (windows, screenFrame) =>"
  },
  {
    "path": "AmethystTests/Model/TestScreen.swift",
    "chars": 1297,
    "preview": "//\n//  TestScreen.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hu"
  },
  {
    "path": "AmethystTests/Model/TestWindow.swift",
    "chars": 1936,
    "preview": "//\n//  TestWindow.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 Ian Ynda-Hu"
  },
  {
    "path": "AmethystTests/Tests/Categories/SIWindow+AmethystTests.swift",
    "chars": 1410,
    "preview": "@testable import Amethyst\nimport Cocoa\nimport Nimble\nimport Quick\n\nclass SIWindowAmethystTests: QuickSpec {\n    override"
  },
  {
    "path": "AmethystTests/Tests/Configuration/UserConfigurationTests.swift",
    "chars": 34402,
    "preview": "//\n//  UserConfigurationTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/15/16.\n//  Copyright © 2016 Ian "
  },
  {
    "path": "AmethystTests/Tests/Layout/BinarySpacePartitioningLayoutTests.swift",
    "chars": 27538,
    "preview": "//\n//  BinarySpacePartitioningLayoutTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 5/29/16.\n//  Copyright"
  },
  {
    "path": "AmethystTests/Tests/Layout/ColumnLayoutTests.swift",
    "chars": 11064,
    "preview": "//\n//  ColumnLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/18/19.\n//  Copyright © 2019 Ian "
  },
  {
    "path": "AmethystTests/Tests/Layout/CustomLayoutTests.swift",
    "chars": 36335,
    "preview": "//\n//  CustomLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 7/8/21.\n//  Copyright © 2021 Ian Y"
  },
  {
    "path": "AmethystTests/Tests/Layout/FloatingLayoutTests.swift",
    "chars": 1899,
    "preview": "//\n//  FloatingLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ia"
  },
  {
    "path": "AmethystTests/Tests/Layout/FullscreenLayoutTests.swift",
    "chars": 3427,
    "preview": "//\n//  FullscreenLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/14/19.\n//  Copyright © 2019 "
  },
  {
    "path": "AmethystTests/Tests/Layout/RowLayoutTests.swift",
    "chars": 11253,
    "preview": "//\n//  RowLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Ynd"
  },
  {
    "path": "AmethystTests/Tests/Layout/TallLayoutTests.swift",
    "chars": 10713,
    "preview": "//\n//  TallLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 9/21/19.\n//  Copyright © 2019 Ian Yn"
  },
  {
    "path": "AmethystTests/Tests/Layout/TallRightLayoutTests.swift",
    "chars": 10757,
    "preview": "//\n//  TallRightLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/18/19.\n//  Copyright © 2019 "
  },
  {
    "path": "AmethystTests/Tests/Layout/ThreeColumnLayoutTests.swift",
    "chars": 35722,
    "preview": "//\n//  ThreeColumnLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/19/19.\n//  Copyright © 201"
  },
  {
    "path": "AmethystTests/Tests/Layout/TwoPaneLayoutTests.swift",
    "chars": 17807,
    "preview": "//\n//  TwoPaneLayoutTests.swift\n//  AmethystTests\n//\n//  Created by @mwz on 14/06/21.\n//  Copyright © 2021 Ian Ynda-Humm"
  },
  {
    "path": "AmethystTests/Tests/Layout/WideLayoutTests.swift",
    "chars": 10698,
    "preview": "//\n//  WideLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/7/19.\n//  Copyright © 2019 Ian Yn"
  },
  {
    "path": "AmethystTests/Tests/Layout/WidescreenTallLayoutTests.swift",
    "chars": 18875,
    "preview": "//\n//  WidescreenTallLayoutTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 12/18/19.\n//  Copyright © "
  },
  {
    "path": "AmethystTests/Tests/Managers/HotKeyManagerTests.swift",
    "chars": 677,
    "preview": "//\n//  HotKeyManagerTests.swift\n//  Amethyst\n//\n//  Created by Ian Ynda-Hummel on 4/18/17.\n//  Copyright © 2017 Ian Ynda"
  },
  {
    "path": "AmethystTests/Tests/Managers/ScreenManagerTests.swift",
    "chars": 9978,
    "preview": "//\n//  ScreenManagerTests.swift\n//  AmethystTests\n//\n//  Created by Ian Ynda-Hummel on 2/11/20.\n//  Copyright © 2020 Ian"
  },
  {
    "path": "Brewfile",
    "chars": 204,
    "preview": "# run 'brew bundle' to install all listed packages\n\n# Build tools\nbrew \"fastlane\" # Easiest way to build and release mob"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3214,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "LICENSE.md",
    "chars": 1059,
    "preview": "Copyright (c) 2015 Ian Ynda-Hummel\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis"
  },
  {
    "path": "README.md",
    "chars": 11655,
    "preview": "# Amethyst\n\n[![Discussions](https://img.shields.io/github/discussions/ianyh/Amethyst)](https://github.com/ianyh/Amethyst"
  },
  {
    "path": "docs/configuration-files.md",
    "chars": 9158,
    "preview": "# Configuration Files\n\nAmethyst will pick up a config file located at `~/.amethyst.yml` or `~/.config/amethyst/amethyst."
  },
  {
    "path": "docs/custom-layouts.md",
    "chars": 6079,
    "preview": "# Custom Layouts (beta)\n\nAmethyst supports implementing custom layouts via JavaScript.\n\n## Installing\n\nLayouts are locat"
  },
  {
    "path": "docs/troubleshooting.md",
    "chars": 1163,
    "preview": "# Troubleshooting\n\n## Nothing is working!\n\nHere are some common problems and their solutions.\n\n### \"Always float\"\n\nAmeth"
  },
  {
    "path": "docs/window-limit.md",
    "chars": 1323,
    "preview": "# Window Limit \n\n## How To Enable\n\nIn the General tab of Amethyst's preferences, there is a Maximum Window Count field. "
  },
  {
    "path": "exportOptions.plist",
    "chars": 372,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "fastlane/Appfile",
    "chars": 230,
    "preview": "# app_identifier(\"[[APP_IDENTIFIER]]\") # The bundle identifier of your app\n# apple_id(\"[[APPLE_ID]]\") # Your Apple email"
  },
  {
    "path": "fastlane/Fastfile",
    "chars": 670,
    "preview": "# This file contains the fastlane.tools configuration\n# You can find the documentation at https://docs.fastlane.tools\n#\n"
  },
  {
    "path": "fastlane/Gymfile",
    "chars": 80,
    "preview": "archive_path(\"./build/Amethyst\")\nscheme(\"Amethyst\")\noutput_directory(\"./build\")\n"
  },
  {
    "path": "fastlane/README.md",
    "chars": 666,
    "preview": "fastlane documentation\n----\n\n# Installation\n\nMake sure you have the latest version of the Xcode command line tools insta"
  },
  {
    "path": "privacy-policy.md",
    "chars": 153,
    "preview": "Amethyst Privacy Policy\n-----------------------\n\nAmethyst does not collect any personal information and does not transmi"
  }
]

About this extraction

This page contains the full source code of the ianyh/Amethyst GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 146 files (900.9 KB), approximately 216.0k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!