nikitabobko/AeroSpace/main 311k tokens More Tools
```
├── .bundle/
   ├── config
├── .editorconfig (omitted)
├── .gitattributes (omitted)
├── .github/
   ├── DISCUSSION_TEMPLATE/
      ├── potential-bugs.yml (100 tokens)
   ├── FUNDING.yml
   ├── ISSUE_TEMPLATE/
      ├── config.yml
      ├── new-issue.yml (100 tokens)
   ├── gh-actions-runner-xcode-select.sh (100 tokens)
   ├── pull_request_template.md (200 tokens)
   ├── workflows/
      ├── build.yml (500 tokens)
      ├── close-third-party-issues.yml (200 tokens)
├── .gitignore (100 tokens)
├── .idea/
   ├── codeStyles/
      ├── Project.xml (100 tokens)
      ├── codeStyleConfig.xml
   ├── inspectionProfiles/
      ├── Project_Default.xml (100 tokens)
├── .swift-version
├── .swiftformat (400 tokens)
├── .swiftlint.yml (500 tokens)
├── AeroSpace.xcodeproj/
   ├── project.pbxproj (2.6k tokens)
   ├── project.xcworkspace/
      ├── contents.xcworkspacedata
├── CONTRIBUTING.md (1200 tokens)
├── Gemfile
├── LICENSE.txt (200 tokens)
├── Package.resolved (300 tokens)
├── Package.swift (600 tokens)
├── README.md (2.2k tokens)
├── ShellParserGenerated/
   ├── .gitignore
   ├── Package.swift (200 tokens)
   ├── Sources/
      ├── ShellParserGenerated/
         ├── ShellLexer.swift (2.3k tokens)
         ├── ShellParser.swift (6.8k tokens)
├── Sources/
   ├── AeroSpaceApp/
      ├── AeroSpaceApp.swift (100 tokens)
   ├── AppBundle/
      ├── GlobalObserver.swift (800 tokens)
      ├── command/
         ├── CmdEnv.swift (200 tokens)
         ├── CmdIo.swift (200 tokens)
         ├── Command.swift (400 tokens)
         ├── cmdManifest.swift (900 tokens)
         ├── cmdResolveTargetOrReportError.swift (300 tokens)
         ├── format.swift (1600 tokens)
         ├── formatToJson.swift (200 tokens)
         ├── impl/
            ├── BalanceSizesCommand.swift (100 tokens)
            ├── CloseAllWindowsButCurrentCommand.swift (200 tokens)
            ├── CloseCommand.swift (300 tokens)
            ├── ConfigCommand.swift (1400 tokens)
            ├── DebugWindowsCommand.swift (1100 tokens)
            ├── EnableCommand.swift (200 tokens)
            ├── ExecAndForgetCommand.swift (100 tokens)
            ├── FlattenWorkspaceTreeCommand.swift (100 tokens)
            ├── FocusBackAndForthCommand.swift (100 tokens)
            ├── FocusCommand.swift (1700 tokens)
            ├── FocusMonitorCommand.swift (700 tokens)
            ├── FullscreenCommand.swift (200 tokens)
            ├── JoinWithCommand.swift (300 tokens)
            ├── LayoutCommand.swift (1000 tokens)
            ├── ListAppsCommand.swift (200 tokens)
            ├── ListExecEnvVarsCommand.swift (100 tokens)
            ├── ListModesCommand.swift (100 tokens)
            ├── ListMonitorsCommand.swift (300 tokens)
            ├── ListWindowsCommand.swift (600 tokens)
            ├── ListWorkspacesCommand.swift (500 tokens)
            ├── MacosNativeFullscreenCommand.swift (400 tokens)
            ├── MacosNativeMinimizeCommand.swift (200 tokens)
            ├── ModeCommand.swift (100 tokens)
            ├── MoveCommand.swift (1500 tokens)
            ├── MoveMouseCommand.swift (500 tokens)
            ├── MoveNodeToMonitorCommand.swift (300 tokens)
            ├── MoveNodeToWorkspaceCommand.swift (400 tokens)
            ├── MoveWorkspaceToMonitorCommand.swift (300 tokens)
            ├── ReloadConfigCommand.swift (300 tokens)
            ├── ResizeCommand.swift (500 tokens)
            ├── SplitCommand.swift (400 tokens)
            ├── SummonWorkspaceCommand.swift (200 tokens)
            ├── SwapCommand.swift (400 tokens)
            ├── TriggerBindingCommand.swift (200 tokens)
            ├── VolumeCommand.swift (200 tokens)
            ├── WorkspaceBackAndForthCommand.swift (100 tokens)
            ├── WorkspaceCommand.swift (500 tokens)
         ├── parseCommand.swift (200 tokens)
      ├── config/
         ├── Config.swift (500 tokens)
         ├── ConfigFile.swift (300 tokens)
         ├── DynamicConfigValue.swift (700 tokens)
         ├── HotkeyBinding.swift (900 tokens)
         ├── Mode.swift (300 tokens)
         ├── keysMap.swift (1900 tokens)
         ├── parseConfig.swift (3.3k tokens)
         ├── parseExecEnvVariables.swift (500 tokens)
         ├── parseGaps.swift (800 tokens)
         ├── parseKeyMapping.swift (500 tokens)
         ├── parseOnWindowDetected.swift (1200 tokens)
         ├── parseWorkspaceToMonitorAssignment.swift (300 tokens)
         ├── startAtLogin.swift (200 tokens)
      ├── focus.swift (1500 tokens)
      ├── focusCache.swift (100 tokens)
      ├── getNativeFocusedWindow.swift (100 tokens)
      ├── initAppBundle.swift (700 tokens)
      ├── layout/
         ├── layoutRecursive.swift (1600 tokens)
         ├── refresh.swift (1400 tokens)
      ├── model/
         ├── Json.swift (400 tokens)
         ├── KnownBundleId.swift (200 tokens)
         ├── Monitor.swift (700 tokens)
         ├── MonitorDescriptionEx.swift (100 tokens)
         ├── MonitorEx.swift (200 tokens)
         ├── Rect.swift (400 tokens)
      ├── mouse/
         ├── mouse.swift (200 tokens)
         ├── moveWithMouse.swift (900 tokens)
         ├── resizeWithMouse.swift (1000 tokens)
      ├── normalizeLayoutReason.swift (700 tokens)
      ├── runLoop.swift (600 tokens)
      ├── server.swift (1000 tokens)
      ├── shell/
         ├── Shell.swift (2.2k tokens)
      ├── tree/
         ├── AbstractApp.swift (100 tokens)
         ├── MacApp.swift (3.4k tokens)
         ├── MacWindow.swift (2.6k tokens)
         ├── MacosUnconventionalWindowsContainer.swift (300 tokens)
         ├── TilingContainer.swift (500 tokens)
         ├── TreeNode.swift (1300 tokens)
         ├── TreeNodeCases.swift (1300 tokens)
         ├── TreeNodeEx.swift (800 tokens)
         ├── Window.swift (600 tokens)
         ├── Workspace.swift (1600 tokens)
         ├── WorkspaceEx.swift (400 tokens)
         ├── frozen/
            ├── FrozenTreeNode.swift (300 tokens)
            ├── FrozenWorld.swift (200 tokens)
            ├── closedWindowsCache.swift (1100 tokens)
         ├── normalizeContainers.swift (300 tokens)
      ├── ui/
         ├── AppearanceTheme.swift (100 tokens)
         ├── ExperimentalUISettings.swift (400 tokens)
         ├── MenuBar.swift (900 tokens)
         ├── MenuBarLabel.swift (1300 tokens)
         ├── MessageView.swift (1000 tokens)
         ├── NSPanelHud.swift (100 tokens)
         ├── SecureInputView.swift (600 tokens)
         ├── TrayMenuModel.swift (800 tokens)
         ├── VolumeView.swift (600 tokens)
      ├── util/
         ├── ArrayEx.swift (200 tokens)
         ├── AxSubscription.swift (400 tokens)
         ├── AxUiElementMock.swift (100 tokens)
         ├── AxUiElementMockEx.swift (1600 tokens)
         ├── LazySequenceProtocolEx.swift
         ├── MruStack.swift (300 tokens)
         ├── NSRunningApplicationEx.swift
         ├── NsApplicationEx.swift (100 tokens)
         ├── SetEx.swift (100 tokens)
         ├── ThreadGuardedValue.swift (200 tokens)
         ├── accessibility.swift (2.4k tokens)
         ├── appBundleUtil.swift (1100 tokens)
         ├── axTrustedCheckOptionPrompt.swift
         ├── dumpAxRecursive.swift (800 tokens)
   ├── AppBundleTests/
      ├── AxWindowKindTest.swift (600 tokens)
      ├── assert.swift (600 tokens)
      ├── command/
         ├── BalanceSizesCommandTest.swift (200 tokens)
         ├── CloseCommandTest.swift (300 tokens)
         ├── ExecCommandTest.swift (100 tokens)
         ├── FlattenWorkspaceTreeCommandTest.swift (200 tokens)
         ├── FocusCommandTest.swift (2k tokens)
         ├── JoinWithCommandTest.swift (200 tokens)
         ├── ListAppsTest.swift (100 tokens)
         ├── ListModesTest.swift (600 tokens)
         ├── ListMonitorsTest.swift (100 tokens)
         ├── ListWindowsTest.swift (900 tokens)
         ├── ListWorkspacesTest.swift (300 tokens)
         ├── MoveCommandTest.swift (2.4k tokens)
         ├── MoveNodeToMonitorCommandTest.swift (200 tokens)
         ├── MoveNodeToWorkspaceCommandTest.swift (700 tokens)
         ├── ResizeCommandTest.swift (300 tokens)
         ├── SplitCommandTest.swift (500 tokens)
         ├── SummonWorkspaceCommandTest.swift (100 tokens)
         ├── SwapCommandTest.swift (1200 tokens)
         ├── WorkspaceCommandTest.swift (300 tokens)
      ├── config/
         ├── ConfigTest.swift (3.2k tokens)
         ├── ParseEnvVariablesTest.swift (500 tokens)
         ├── SplitArgsTest.swift (200 tokens)
      ├── model/
         ├── ClientServerTest.swift (200 tokens)
      ├── shell/
         ├── ShellTest.swift (1000 tokens)
      ├── testExtensions.swift
      ├── testUtil.swift (700 tokens)
      ├── tree/
         ├── TestApp.swift (200 tokens)
         ├── TestWindow.swift (300 tokens)
         ├── TilingContainer.swift (100 tokens)
         ├── TreeNodeTest.swift (700 tokens)
   ├── Cli/
      ├── _main.swift (900 tokens)
      ├── cliUtil.swift
      ├── subcommandDescriptionsGenerated.swift (600 tokens)
   ├── Common/
      ├── appMetadata.swift (100 tokens)
      ├── cmdArgs/
         ├── ArgParser.swift (600 tokens)
         ├── ArgParserInput.swift (100 tokens)
         ├── SubArgParser.swift (700 tokens)
         ├── cmdArgsManifest.swift (1200 tokens)
         ├── impl/
            ├── BalanceSizesCmdArgs.swift (100 tokens)
            ├── CloseAllWindowsButCurrentCmdArgs.swift (200 tokens)
            ├── CloseCmdArgs.swift (100 tokens)
            ├── ConfigCmdArgs.swift (500 tokens)
            ├── DebugWindowsCmdArgs.swift (100 tokens)
            ├── EnableCmdArgs.swift (300 tokens)
            ├── ExecAndForgetCmdArgs.swift (100 tokens)
            ├── FlattenWorkspaceTreeCmdArgs.swift (100 tokens)
            ├── FocusBackAndForthCmdArgs.swift (100 tokens)
            ├── FocusCmdArgs.swift (1000 tokens)
            ├── FocusMonitorCmdArgs.swift (500 tokens)
            ├── FullscreenCmdArgs.swift (200 tokens)
            ├── JoinWithCmdArgs.swift (200 tokens)
            ├── LayoutCmdArgs.swift (500 tokens)
            ├── ListAppsCmdArgs.swift (500 tokens)
            ├── ListExecEnvVarsCmdArgs.swift (100 tokens)
            ├── ListModesCmdArgs.swift (200 tokens)
            ├── ListMonitorsCmdArgs.swift (300 tokens)
            ├── ListWindowsCmdArgs.swift (1600 tokens)
            ├── ListWorkspacesCmdArgs.swift (900 tokens)
            ├── MacosNativeFullscreenCmdArgs.swift (300 tokens)
            ├── MacosNativeMinimizeCmdArgs.swift (100 tokens)
            ├── ModeCmdArgs.swift (100 tokens)
            ├── MoveCmdArgs.swift (500 tokens)
            ├── MoveMouseCmdArgs.swift (300 tokens)
            ├── MoveNodeToMonitorCmdArgs.swift (300 tokens)
            ├── MoveNodeToWorkspaceCmdArgs.swift (500 tokens)
            ├── MoveWorkpsaceToMonitorCmdArgs.swift (200 tokens)
            ├── ReloadConfigCmdArgs.swift (100 tokens)
            ├── ResizeCmdArgs.swift (400 tokens)
            ├── SplitCmdArgs.swift (200 tokens)
            ├── SummonWorkspaceCmdArgs.swift (200 tokens)
            ├── SwapCmdArgs.swift (200 tokens)
            ├── TriggerBindingCmdArgs.swift (200 tokens)
            ├── VolumeCmdArgs.swift (400 tokens)
            ├── WorkspaceBackAndForthCmdArgs.swift (100 tokens)
            ├── WorkspaceCmdArgs.swift (600 tokens)
         ├── parseCmdArgs.swift (500 tokens)
         ├── parseSpecificCmdArgs.swift (900 tokens)
         ├── splitArgs.swift (600 tokens)
         ├── subcommandParsers.swift (100 tokens)
      ├── cmdHelpGenerated.swift (1500 tokens)
      ├── gitHashGenerated.swift
      ├── model/
         ├── AxAppThreadToken.swift (100 tokens)
         ├── CardinalDirection.swift (100 tokens)
         ├── CardinalOrDfsDirection.swift (200 tokens)
         ├── DfsNextPrev.swift
         ├── Init.swift (100 tokens)
         ├── MonitorDescription.swift (400 tokens)
         ├── NextPrev.swift
         ├── Orientation.swift (100 tokens)
         ├── WorkspaceName.swift (300 tokens)
         ├── clientServer.swift (300 tokens)
         ├── sponsorshipPrompts.swift (100 tokens)
      ├── util/
         ├── AeroAny.swift (200 tokens)
         ├── ArrSlice.swift (700 tokens)
         ├── BoolEx.swift (100 tokens)
         ├── CollectionEx.swift (100 tokens)
         ├── ComparableEx.swift
         ├── ConvenienceCopyable.swift
         ├── EquatableNoop.swift (100 tokens)
         ├── JsonEncoderEx.swift (100 tokens)
         ├── Lateinit.swift (100 tokens)
         ├── OptionalEx.swift (400 tokens)
         ├── ResultEx.swift (300 tokens)
         ├── SequenceEx.swift (700 tokens)
         ├── StringEx.swift (1300 tokens)
         ├── StringLogicalSegments.swift (300 tokens)
         ├── commonUtil.swift (1300 tokens)
         ├── showMessageInGui.swift (200 tokens)
      ├── versionGenerated.swift
   ├── PrivateApi/
      ├── include/
         ├── module.modulemap
         ├── private.h (200 tokens)
         ├── private.m
├── axDumps/
   ├── about_this_mac.json5 (900 tokens)
   ├── alacritty_decorations_buttonless.json5 (900 tokens)
   ├── apple_calendar.json5 (1100 tokens)
   ├── apple_calendar_settings.json5 (1000 tokens)
   ├── apple_followup_sign_in_to_a_new_device_confirmation.json5 (700 tokens)
   ├── apple_mail.json5 (1500 tokens)
   ├── apple_mail_new_email.json5 (1100 tokens)
   ├── apple_mail_settings.json5 (1000 tokens)
   ├── archiveutility.json5 (1000 tokens)
   ├── calculator.json5 (1000 tokens)
   ├── choose_1_5_0.json5 (700 tokens)
   ├── chrome.json5 (1100 tokens)
   ├── chrome_choose_what_to_share_popup.json5 (1000 tokens)
   ├── chrome_extensions_popup.json5 (600 tokens)
   ├── chrome_find_in_page.json5 (600 tokens)
   ├── chrome_pip.json5 (600 tokens)
   ├── chrome_sharing_is_in_progress_popup.json5 (600 tokens)
   ├── drracket.json5 (1200 tokens)
   ├── finder.json5 (1300 tokens)
   ├── finder_quick_look.json5 (900 tokens)
   ├── firefox.json5 (1000 tokens)
   ├── firefox_extensions_popup.json5 (700 tokens)
   ├── firefox_mouse_hover_extensions.json5 (600 tokens)
   ├── firefox_mouse_hover_tab.json5 (600 tokens)
   ├── firefox_non_native_fullscreen.json5 (600 tokens)
   ├── firefox_normal_window_when_non_native_fullscreen_in_background.json5 (1000 tokens)
   ├── firefox_pinterest_sign_in_with_google.json5 (1100 tokens)
   ├── firefox_pip.json5 (1100 tokens)
   ├── ghostty.json5 (1100 tokens)
   ├── ghostty_about.json5 (900 tokens)
   ├── ghostty_check_for_updates.json5 (700 tokens)
   ├── ghostty_config_error.json5 (1000 tokens)
   ├── ghostty_window_decorations_false.json5 (600 tokens)
   ├── intellij.json5 (1400 tokens)
   ├── intellij_background_tasks.json5 (700 tokens)
   ├── intellij_context_menu.json5 (700 tokens)
   ├── intellij_native_open_window.json5 (600 tokens)
   ├── intellij_quick_doc_popup.json5 (700 tokens)
   ├── intellij_rebase_dialog.json5 (1000 tokens)
   ├── iphonesimulator.json5 (1200 tokens)
   ├── jetbrains_toolbox.json5 (600 tokens)
   ├── karabiner_event_viewer.json5 (1100 tokens)
   ├── karabiner_settings.json5 (1100 tokens)
   ├── macos_capslock_popup_safari.json5 (600 tokens)
   ├── macos_capslock_popup_textedit.json5 (500 tokens)
   ├── macos_share_window_purple_pill_sublime.json5 (600 tokens)
   ├── marta.json5 (1100 tokens)
   ├── mpv_fullscreen.json5 (600 tokens)
   ├── mpv_windowed.json5 (1300 tokens)
   ├── nomachine_session_1.json5 (600 tokens)
   ├── nomachine_session_2.json5 (1000 tokens)
   ├── nomachine_welcome_window_1.json5 (600 tokens)
   ├── nomachine_welcome_window_2.json5 (1000 tokens)
   ├── qutebrowser.json5 (1200 tokens)
   ├── qutebrowser_context_menu.json5 (600 tokens)
   ├── qutebrowser_hide_decoration.json5 (600 tokens)
   ├── raycast.json5 (500 tokens)
   ├── safari.json5 (1100 tokens)
   ├── safari_pinterest_sign_in_with_google.json5 (1300 tokens)
   ├── scenario_firefox_google_meet_share_window/
      ├── 01_firefox.json5 (300 tokens)
      ├── 02_firefox.json5 (600 tokens)
      ├── 03_firefox.json5 (300 tokens)
      ├── 04_firefox.json5 (600 tokens)
      ├── 05_apple_controlcenter.json5 (300 tokens)
      ├── 06_firefox.json5 (400 tokens)
      ├── 07_firefox.json5 (600 tokens)
      ├── README.md (100 tokens)
   ├── slack.json5 (1300 tokens)
   ├── slack_chat_in_a_separate_window.json5 (1300 tokens)
   ├── slack_huddle_share_screen_draw_on_screen_fake_window.json5 (1200 tokens)
   ├── slack_huddle_share_screen_floating_popup.json5 (1100 tokens)
   ├── spotify.json5 (1000 tokens)
   ├── steam_1.json5 (700 tokens)
   ├── steam_2.json5 (700 tokens)
   ├── sublime_text_4.json5 (1000 tokens)
   ├── system_settings.json5 (1000 tokens)
   ├── telegram.json5 (1100 tokens)
   ├── telegram_image_viewer.json5 (600 tokens)
   ├── terminal_app.json5 (1100 tokens)
   ├── transmission.json5 (1100 tokens)
   ├── transmission_torrent_inspector.json5 (1000 tokens)
   ├── vlc_empty.json5 (900 tokens)
   ├── vlc_fullscreen.json5 (500 tokens)
   ├── vlc_video_playing.json5 (1200 tokens)
   ├── vs_code.json5 (1000 tokens)
   ├── vs_code_nativeFullScreen_false.json5 (1000 tokens)
   ├── vs_codium.json5 (1100 tokens)
   ├── vs_codium_nativeFullScreen_false.json5 (900 tokens)
   ├── xcode.json5 (1200 tokens)
   ├── xcode_build_succeeded_popup.json5 (600 tokens)
   ├── xcode_installing_system_components.json5 (500 tokens)
   ├── zed.json5 (1100 tokens)
├── build-debug.sh (100 tokens)
├── build-docs.sh (200 tokens)
├── build-release.sh (900 tokens)
├── build-shell-completion.sh (200 tokens)
├── dev-docs/
   ├── architecture.md (500 tokens)
   ├── development.md (900 tokens)
├── docs/
   ├── aerospace-balance-sizes.adoc (200 tokens)
   ├── aerospace-close-all-windows-but-current.adoc (200 tokens)
   ├── aerospace-close.adoc (200 tokens)
   ├── aerospace-config.adoc (500 tokens)
   ├── aerospace-debug-windows.adoc (300 tokens)
   ├── aerospace-enable.adoc (200 tokens)
   ├── aerospace-exec-and-forget.adoc (200 tokens)
   ├── aerospace-flatten-workspace-tree.adoc (200 tokens)
   ├── aerospace-focus-back-and-forth.adoc (300 tokens)
   ├── aerospace-focus-monitor.adoc (300 tokens)
   ├── aerospace-focus.adoc (600 tokens)
   ├── aerospace-fullscreen.adoc (300 tokens)
   ├── aerospace-join-with.adoc (300 tokens)
   ├── aerospace-layout.adoc (400 tokens)
   ├── aerospace-list-apps.adoc (500 tokens)
   ├── aerospace-list-exec-env-vars.adoc (200 tokens)
   ├── aerospace-list-modes.adoc (200 tokens)
   ├── aerospace-list-monitors.adoc (500 tokens)
   ├── aerospace-list-windows.adoc (1000 tokens)
   ├── aerospace-list-workspaces.adoc (700 tokens)
   ├── aerospace-macos-native-fullscreen.adoc (300 tokens)
   ├── aerospace-macos-native-minimize.adoc (200 tokens)
   ├── aerospace-mode.adoc (200 tokens)
   ├── aerospace-move-mouse.adoc (400 tokens)
   ├── aerospace-move-node-to-monitor.adoc (400 tokens)
   ├── aerospace-move-node-to-workspace.adoc (400 tokens)
   ├── aerospace-move-workspace-to-monitor.adoc (400 tokens)
   ├── aerospace-move.adoc (600 tokens)
   ├── aerospace-reload-config.adoc (200 tokens)
   ├── aerospace-resize.adoc (300 tokens)
   ├── aerospace-split.adoc (300 tokens)
   ├── aerospace-summon-workspace.adoc (300 tokens)
   ├── aerospace-swap.adoc (300 tokens)
   ├── aerospace-trigger-binding.adoc (300 tokens)
   ├── aerospace-volume.adoc (200 tokens)
   ├── aerospace-workspace-back-and-forth.adoc (200 tokens)
   ├── aerospace-workspace.adoc (400 tokens)
   ├── aerospace.adoc (100 tokens)
   ├── assets/
      ├── h_accordion.png
      ├── h_tiles.png
      ├── icon.png
      ├── monitor-arrangement-1-bad.svg (19.8k tokens)
      ├── monitor-arrangement-1-good.svg (11.6k tokens)
      ├── monitor-arrangement-2-bad.svg (12.4k tokens)
      ├── monitor-arrangement-2-good.svg (7.5k tokens)
      ├── tree.png
      ├── v_accordion.png
   ├── commands.adoc (1400 tokens)
   ├── config-examples/
      ├── default-config.toml (1600 tokens)
      ├── i3-like-config-example.toml (600 tokens)
   ├── goodies.adoc (1700 tokens)
   ├── guide.adoc (5.6k tokens)
   ├── index.html (100 tokens)
   ├── util/
      ├── all-monitors-option.adoc (100 tokens)
      ├── conditional-arguments-header.adoc
      ├── conditional-examples-header.adoc
      ├── conditional-exit-code-header.adoc
      ├── conditional-options-header.adoc
      ├── conditional-output-format-header.adoc
      ├── header.adoc (100 tokens)
      ├── man-attributes.adoc
      ├── man-footer.adoc (100 tokens)
      ├── monitor-option.adoc (100 tokens)
      ├── site-attributes.adoc
      ├── window-id-flag-desc.adoc
      ├── workspace-flag-desc.adoc
├── format.sh
├── generate-shell-parser.sh (200 tokens)
├── generate.sh (500 tokens)
├── grammar/
   ├── ShellLexer.g4 (300 tokens)
   ├── ShellParser.g4 (500 tokens)
   ├── commands-bnf-grammar.txt (1300 tokens)
├── install-from-sources.sh (200 tokens)
├── legal/
   ├── LICENSE.txt
   ├── README.md (400 tokens)
   ├── third-party-license/
      ├── LICENSE-BlueSocket.txt (2k tokens)
      ├── LICENSE-HotKey.txt (200 tokens)
      ├── LICENSE-ISSoundAdditions.txt (200 tokens)
      ├── LICENSE-TOMLKIT.txt (200 tokens)
      ├── LICENSE-antlr.txt (300 tokens)
      ├── LICENSE-swift-collections.txt (2.4k tokens)
      ├── LICENSE-tomlplusplus.txt (200 tokens)
├── makefile
├── project.yml (400 tokens)
├── resources/
   ├── AeroSpace.entitlements
   ├── Assets.xcassets/
      ├── AccentColor.colorset/
         ├── Contents.json
      ├── AppIcon.appiconset/
         ├── Contents.json (200 tokens)
         ├── icon.png
      ├── Contents.json
├── run-cli.sh
├── run-debug.sh
├── run-swift-test.sh
├── run-tests.sh (100 tokens)
├── script/
   ├── build-brew-cask.sh (500 tokens)
   ├── check-uncommitted-files.sh (100 tokens)
   ├── clean-project.sh
   ├── clean-xcode.sh
   ├── generate-cmd-help.sh (200 tokens)
   ├── install-dep.sh (800 tokens)
   ├── publish-release.sh (300 tokens)
   ├── reset-accessibility-permission-for-debug.sh (100 tokens)
   ├── setup.sh (600 tokens)
```


## /.bundle/config

```bundle/config path="/.bundle/config" 
---
BUNDLE_PATH: ".deps/bundler-path"
BUNDLE_DISABLE_SHARED_GEMS: "1"

```

## /.github/DISCUSSION_TEMPLATE/potential-bugs.yml

```yml path="/.github/DISCUSSION_TEMPLATE/potential-bugs.yml" 
body:
  - type: textarea
    id: body
    attributes:
      label: Body
      value: |
        Steps to reproduce:
        1.
        2.
        3.

        Expected result:
        Actual result:

        ### Additional info

        \`\`\`shell
        $ aerospace -v
        \`\`\`
    validations:
      required: true

```

## /.github/FUNDING.yml

```yml path="/.github/FUNDING.yml" 
github: [nikitabobko]

```

## /.github/ISSUE_TEMPLATE/config.yml

```yml path="/.github/ISSUE_TEMPLATE/config.yml" 
blank_issues_enabled: false

```

## /.github/ISSUE_TEMPLATE/new-issue.yml

```yml path="/.github/ISSUE_TEMPLATE/new-issue.yml" 
name: New Issue
description: New Issue
body:
  - type: markdown
    attributes:
      value: |
        AeroSpace project doesn't accept Issues directly. Please prefer GitHub Discussions. See: https://github.com/nikitabobko/AeroSpace/issues/947
  - type: checkboxes
    id: checkbox
    attributes:
      label: 'Sanity check'
      options:
        - label: |
            I read https://github.com/nikitabobko/AeroSpace/issues/947
          required: true
  - type: textarea
    id: body
    attributes:
      label: Body
      value: |
        YOUR ISSUE WILL BE IMMEDIATELY CLOSED BY BOT, DON'T OPEN NEW ISSUES DIRECTLY, USE DISCUSSIONS INSTEAD
    validations:
      required: true

```

## /.github/gh-actions-runner-xcode-select.sh

```sh path="/.github/gh-actions-runner-xcode-select.sh" 
#!/bin/bash
set -e # Exit if one of commands exit with non-zero exit code
set -u # Treat unset variables and parameters other than the special parameters ‘@’ or ‘*’ as an error
set -o pipefail # Any command failed in the pipe fails the whole pipe
# set -x # Print shell commands as they are executed (or you can try -v which is less verbose)

sw_vers -productVersion
# Xcode version affects the target macOS SDK that we compile against + different Xcodes bundle different Swift verions
if sw_vers -productVersion | grep -q "^14"; then # macOS 14
  sudo xcode-select -s "$XCODE_16_DEVELOPER_DIR"
else
  sudo xcode-select -s "$XCODE_26_DEVELOPER_DIR"
fi

```

## /.github/pull_request_template.md

## PR checklist

- [ ] Explain your changes in the relevant commit messages rather than in the PR description. The PR description must not contain more information than the commit messages (except for images and other media).
- [ ] Each commit must explain what/why/how and motivation in its description. https://cbea.ms/git-commit/
- [ ] Don't forget to link the appropriate issues/discussions in commit messages (if applicable).
- [ ] Each commit must be an atomic change (a PR may contain several commits). Don't introduce new functional changes together with refactorings in the same commit.
- [ ] The GitHub Actions CI must pass (you can fix failures after submitting a PR).

Failure to follow the checklist with no apparent reasons will result in silent PR rejection.


## /.github/workflows/build.yml

```yml path="/.github/workflows/build.yml" 
name: build

on:
  push:
    branches:
      - 'main'
      - 'rr/**' # "rr" stands for "remote run"
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '0 0 * * *' # every day at 00:00

jobs:

  build-debug:
    strategy:
      # fail-fast: false # Disable fail-fast in matrix
      matrix:
        # https://docs.github.com/en/actions/reference/runners/github-hosted-runners#standard-github-hosted-runners-for-public-repositories
        # https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md
        # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-arm64-Readme.md
        # https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
        #
        # Xcode versions:
        # - https://en.wikipedia.org/wiki/Xcode
        # - https://xcodereleases.com/?scope=release
        os: [macos-14, macos-15, macos-26]
    name: build-debug
    runs-on: ${{ matrix.os }}
    steps:
      - run: env # Debug
      - uses: actions/checkout@v3
      - run: ./.github/gh-actions-runner-xcode-select.sh
      - run: brew install swiftly
      - run: swiftly init --skip-install --assume-yes --verbose && swiftly install
      - run: ./build-debug.sh
      - run: ./run-tests.sh

  # We build release artifacts only on the latest macOS versions because:
  # 1. It cuts the build time twice on GH Actions
  # 2. The latest Xcode version is not available on old macOS, and old Xcode versions bundle too old Swift version
  build-release:
    strategy:
      # fail-fast: false # Disable fail-fast in matrix
      matrix:
        os: [macos-15, macos-26]
    name: build-release
    runs-on: ${{ matrix.os }}
    steps:
      - run: env # Debug
      - uses: actions/checkout@v3
      - run: ./.github/gh-actions-runner-xcode-select.sh
      - run: brew install bash fish xcbeautify swiftly
      - run: swiftly init --skip-install --assume-yes --verbose && swiftly install
      - name: './build-release.sh'
        run: |
          # "-" means "Sign to run locally". There is no aerospace-codesign-certificate on GH Actions
          ./build-release.sh --codesign-identity -
          ./install-from-sources.sh --dont-rebuild
      - name: cat ./.release/xcodebuild.log
        if: ${{ always() }}
        run: 'if test -f ./.release/xcodebuild.log; then cat ./.release/xcodebuild.log; fi'

```

## /.github/workflows/close-third-party-issues.yml

```yml path="/.github/workflows/close-third-party-issues.yml" 
name: close-third-party-issues

on:
  issues:
    types: [opened]

jobs:
  close-third-party-issues:
    name: Close third party issues
    runs-on: ubuntu-latest
    permissions:
      issues: write
    steps:
    - name: Close third party issues
      run: |
        set -e # Exit if one of commands exit with non-zero exit code
        set -u # Treat unset variables and parameters other than the special parameters ‘@’ or ‘*’ as an error
        set -o pipefail # Any command failed in the pipe fails the whole pipe

        author="$(gh issue view "$ISSUE" --json author --jq '.author.login')"

        close() {
          gh issue edit "$ISSUE" --add-label bin
          gh issue close "$ISSUE" --comment "Please don't open issues directly, use GitHub Discussions instead. See: https://github.com/nikitabobko/AeroSpace/issues/947"
          gh issue lock "$ISSUE"
        }

        test "$author" = nikitabobko || test "$author" = mobile-ar || close
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        GH_REPO: ${{ github.repository }}
        ISSUE: ${{ github.event.issue.number }}

```

## /.gitignore

```gitignore path="/.gitignore" 
/.idea
/.debug
/.release
/.shell-completion
/.site
/.man
/Gemfile.lock
# IDK, AppCode randomly creates this EMPTY file. I have no idea what this is
/default.profraw
.DS_Store
/.xcode-build
# Swift package manager
/.build
/.swiftpm
/.vscode
# External dependencies
/.deps

# For whatever local files that developers might want to keep there (I personally keep a separate `generated-html` git worktree here)
/.local

# XCode User settings
xcuserdata/
xcshareddata/

```

## /.idea/codeStyles/Project.xml

```xml path="/.idea/codeStyles/Project.xml" 
<component name="ProjectCodeStyleConfiguration">
  <code_scheme name="Project" version="173">
    <SwiftCodeStyleSettings>
      <option name="METHOD_CHAIN_INDENT" value="4" />
    </SwiftCodeStyleSettings>
    <codeStyleSettings language="Swift">
      <option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
      <option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
      <option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
      <option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
      <indentOptions>
        <option name="CONTINUATION_INDENT_SIZE" value="4" />
      </indentOptions>
    </codeStyleSettings>
  </code_scheme>
</component>
```

## /.idea/codeStyles/codeStyleConfig.xml

```xml path="/.idea/codeStyles/codeStyleConfig.xml" 
<component name="ProjectCodeStyleConfiguration">
  <state>
    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
  </state>
</component>
```

## /.idea/inspectionProfiles/Project_Default.xml

```xml path="/.idea/inspectionProfiles/Project_Default.xml" 
<component name="InspectionProjectProfileManager">
  <profile version="1.0">
    <option name="myName" value="Project Default" />
    <inspection_tool class="SwiftUnnecessarySelfQualifier" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
  </profile>
</component>
```

## /.swift-version

```swift-version path="/.swift-version" 
6.2.0

```

## /.swiftformat

```swiftformat path="/.swiftformat" 
# https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md
--exclude ./ShellParserGenerated
--indentcase true
--patternlet inline
--indentstrings true

# https://github.com/nicklockwood/SwiftFormat/issues/483 fix indentation for expressions nested in ternary
--wrapternary before-operators

--disable all

# --enable docComments

--enable anyObjectProtocol
--enable blankLineAfterImports
--enable blankLinesAtEndOfScope
--enable blankLinesBetweenImports
--enable conditionalAssignment
--enable docCommentsBeforeModifiers
--enable duplicateImports
--enable elseOnSameLine
--enable emptyBraces
--enable emptyExtensions
--enable enumNamespaces
--enable fileMacro
--enable genericExtensions
--enable indent
--enable leadingDelimiters
--enable linebreakAtEndOfFile
--enable linebreaks
--enable modifierOrder
--enable numberFormatting
--enable preferCountWhere
--enable preferFinalClasses
--enable redundantAsync
--enable redundantBackticks
--enable redundantBreak
--enable redundantClosure
--enable redundantEquatable
--enable redundantFileprivate
--enable redundantGet
--enable redundantLet
--enable redundantLetError
--enable redundantMemberwiseInit
--enable redundantObjc
--enable redundantOptionalBinding
--enable redundantThrows
--enable redundantTypedThrows
--enable redundantVoidReturnType
--enable semicolons
--enable spaceAroundBraces
--enable spaceAroundBrackets
--enable spaceAroundGenerics
--enable spaceAroundOperators
--enable spaceAroundParens
--enable spaceInsideBraces
--enable spaceInsideBrackets
--enable spaceInsideGenerics
--enable spaceInsideParens
--enable strongOutlets
--enable trailingClosures
--enable trailingCommas
--enable trailingSpace
--enable urlMacro
--enable wrapMultilineStatementBraces

--enable extensionAccessControl
--extensionacl on-declarations

```

## /.swiftlint.yml

```yml path="/.swiftlint.yml" 
excluded:
  - .build
  - .xcode-build
  - ./ShellParserGenerated

# https://realm.github.io/SwiftLint/rule-directory.html
only_rules:
  # - line_length # todo fix it
  - colon
  - computed_accessors_order
  - dynamic_inline # Avoid using ‘dynamic’ and ‘@inline(__always)’ together
  - empty_enum_arguments
  - empty_parameters # Prefer () -> over Void ->
  - empty_string # Prefer checking isEmpty over comparing string to an empty string literal
  - file_name_no_space
  - first_where
  - implicit_getter
  - is_disjoint
  - last_where # Prefer using .last(where:) over .filter { }.last in collections
  - legacy_constant
  - legacy_constructor # Swift constructors are preferred over legacy convenience functions
  - legacy_hashing # Prefer using the hash(into:) function instead of overriding hashValue
  - legacy_nsgeometry_functions # Struct extension properties and methods are preferred over legacy functions
  - legacy_random # Prefer using type.random(in:) over legacy functions
  - local_doc_comment # Prefer regular comments over doc comments in local scopes
  - modifier_order
  - no_fallthrough_only
  - no_space_in_method_call
  - nsobject_prefer_isequal # NSObject subclasses should implement isEqual instead of ==
  - function_name_whitespace
  - optional_enum_case_matching
  - redundant_discardable_let
  - redundant_nil_coalescing # nil coalescing operator is only evaluated if the lhs is nil, coalescing operator with nil as rhs is redundant
  - switch_case_alignment
  - toggle_bool
  - trailing_newline
  - trailing_semicolon # Lines should not have trailing semicolons
  - trailing_whitespace # Lines should not have trailing whitespace
  - unavailable_condition # Use #unavailable/#available instead of #available/#unavailable with an empty body.
  - unneeded_break_in_switch # Avoid using unneeded break statements
  - unneeded_override # Remove overridden functions that don’t do anything except call their super
  # - unused_closure_parameter
  - unused_control_flow_label
  - unused_enumerated # When the index or the item is not used, .enumerated() can be removed.
  - unused_optional_binding # Prefer != nil over let _ =
  - unused_setter_value
  - weak_delegate # Delegates should be weak to avoid reference cycles

switch_case_alignment:
  indented_cases: true

strict: true
# quiet: true

```

## /AeroSpace.xcodeproj/project.pbxproj

```pbxproj path="/AeroSpace.xcodeproj/project.pbxproj" 
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 77;
	objects = {

/* Begin PBXBuildFile section */
		238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */ = {isa = PBXBuildFile; fileRef = 8FE45A887100EB70912B07F0 /* default-config.toml */; };
		852F88894A3B9FC385563665 /* AppBundle in Frameworks */ = {isa = PBXBuildFile; productRef = 018E55979F61DA6DA6DCB442 /* AppBundle */; };
		883A44C8295FECF49F94269D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */; };
		C40E0D9C06086C58955237D9 /* AeroSpaceApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
		09685297933511208058F7CF /* AeroSpace.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AeroSpace.app; sourceTree = BUILT_PRODUCTS_DIR; };
		18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AeroSpaceApp.swift; sourceTree = "<group>"; };
		6606D24B4B23E9582CFA3B86 /* AeroSpace */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AeroSpace; path = .; sourceTree = SOURCE_ROOT; };
		84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		8FE45A887100EB70912B07F0 /* default-config.toml */ = {isa = PBXFileReference; path = "default-config.toml"; sourceTree = "<group>"; };
		CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AeroSpace.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		2AFAB0BC1A2742132D7CB950 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				852F88894A3B9FC385563665 /* AppBundle in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		0E0109AE5F7881520B0D2384 /* config-examples */ = {
			isa = PBXGroup;
			children = (
				8FE45A887100EB70912B07F0 /* default-config.toml */,
			);
			name = "config-examples";
			path = "docs/config-examples";
			sourceTree = "<group>";
		};
		21E15F84087042E63C0150AB /* resources */ = {
			isa = PBXGroup;
			children = (
				CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */,
				84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */,
			);
			path = resources;
			sourceTree = "<group>";
		};
		393942C56466FDBBE35F9EC0 = {
			isa = PBXGroup;
			children = (
				6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */,
				0E0109AE5F7881520B0D2384 /* config-examples */,
				3A1FF786C84025133F96138D /* Packages */,
				21E15F84087042E63C0150AB /* resources */,
				62BEA6F49E6648E2EE3C208F /* Products */,
			);
			sourceTree = "<group>";
		};
		3A1FF786C84025133F96138D /* Packages */ = {
			isa = PBXGroup;
			children = (
				6606D24B4B23E9582CFA3B86 /* AeroSpace */,
			);
			name = Packages;
			sourceTree = "<group>";
		};
		62BEA6F49E6648E2EE3C208F /* Products */ = {
			isa = PBXGroup;
			children = (
				09685297933511208058F7CF /* AeroSpace.app */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */ = {
			isa = PBXGroup;
			children = (
				18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */,
			);
			name = AeroSpaceApp;
			path = Sources/AeroSpaceApp;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		B00BE37A79171B0EE995EB83 /* AeroSpace */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 1C34EA41A1F045E016D1944D /* Build configuration list for PBXNativeTarget "AeroSpace" */;
			buildPhases = (
				D7A18303C03F2CB26F7BB54B /* Sources */,
				BA5F2F9022B8385637D263E4 /* Resources */,
				2AFAB0BC1A2742132D7CB950 /* Frameworks */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = AeroSpace;
			packageProductDependencies = (
				018E55979F61DA6DA6DCB442 /* AppBundle */,
			);
			productName = AeroSpace;
			productReference = 09685297933511208058F7CF /* AeroSpace.app */;
			productType = "com.apple.product-type.application";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		0B585B3093DA0FC12E7983E2 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = YES;
				LastUpgradeCheck = 1430;
			};
			buildConfigurationList = D6982B0C3E92C5AF28BCD315 /* Build configuration list for PBXProject "AeroSpace" */;
			compatibilityVersion = "Xcode 14.0";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				Base,
				en,
			);
			mainGroup = 393942C56466FDBBE35F9EC0;
			minimizedProjectReferenceProxies = 1;
			packageReferences = (
				9A00429279948F2879C9FE30 /* XCLocalSwiftPackageReference "." */,
			);
			preferredProjectObjectVersion = 77;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				B00BE37A79171B0EE995EB83 /* AeroSpace */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		BA5F2F9022B8385637D263E4 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				883A44C8295FECF49F94269D /* Assets.xcassets in Resources */,
				238EF26CAAADD1FE11312D7C /* default-config.toml in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		D7A18303C03F2CB26F7BB54B /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				C40E0D9C06086C58955237D9 /* AeroSpaceApp.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin XCBuildConfiguration section */
		175127AAF914899705FABF12 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"$(inherited)",
					"DEBUG=1",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SDKROOT = macosx;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		31B702864571F51814E4F12C /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = resources/AeroSpace.entitlements;
				CODE_SIGN_IDENTITY = "aerospace-codesign-certificate";
				COMBINE_HIDPI_IMAGES = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_LSUIElement = YES;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 13.0;
				MARKETING_VERSION = "0.0.0-SNAPSHOT";
				PRODUCT_BUNDLE_IDENTIFIER = bobko.aerospace.debug;
				PRODUCT_NAME = "AeroSpace-Debug";
				SDKROOT = macosx;
				SWIFT_VERSION = 6.2;
			};
			name = Debug;
		};
		A991F90908318BCD1655E904 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
		D1D1A9E07F0AB40E14CAC0F6 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_ENTITLEMENTS = resources/AeroSpace.entitlements;
				CODE_SIGN_IDENTITY = "aerospace-codesign-certificate";
				COMBINE_HIDPI_IMAGES = YES;
				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_LSUIElement = YES;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 13.0;
				MARKETING_VERSION = "0.0.0-SNAPSHOT";
				PRODUCT_BUNDLE_IDENTIFIER = bobko.aerospace;
				PRODUCT_NAME = AeroSpace;
				SDKROOT = macosx;
				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
				SWIFT_VERSION = 6.2;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		1C34EA41A1F045E016D1944D /* Build configuration list for PBXNativeTarget "AeroSpace" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				31B702864571F51814E4F12C /* Debug */,
				D1D1A9E07F0AB40E14CAC0F6 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Debug;
		};
		D6982B0C3E92C5AF28BCD315 /* Build configuration list for PBXProject "AeroSpace" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				175127AAF914899705FABF12 /* Debug */,
				A991F90908318BCD1655E904 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Debug;
		};
/* End XCConfigurationList section */

/* Begin XCLocalSwiftPackageReference section */
		9A00429279948F2879C9FE30 /* XCLocalSwiftPackageReference "." */ = {
			isa = XCLocalSwiftPackageReference;
			relativePath = .;
		};
/* End XCLocalSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
		018E55979F61DA6DA6DCB442 /* AppBundle */ = {
			isa = XCSwiftPackageProductDependency;
			productName = AppBundle;
		};
/* End XCSwiftPackageProductDependency section */
	};
	rootObject = 0B585B3093DA0FC12E7983E2 /* Project object */;
}

```

## /AeroSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata

```xcworkspacedata path="/AeroSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata" 
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>

```

## /CONTRIBUTING.md

# Contributing

## Users cannot create GitHub Issues directly

AeroSpace project doesn't accept Issues directly - we ask you to create a [Discussion](https://github.com/nikitabobko/AeroSpace/discussions) first.

The submitted Issues are often either obvious duplicates, environmental problems, or configuration errors by the users themselves.
For a hobby project, I don't have enough time and energy to process every such submitted Issue.

As an alternative, you can start a Discussion on [GitHub discussions](https://github.com/nikitabobko/AeroSpace/discussions) forum.
Any Discussion which clearly identifies a problem and can be confirmed or reproduced will be converted to an Issue by maintainers.

It's users' responsibility to minimize their bugs as much as possible.
All users play a part in bugs reproduction.

In general the flow is the following:
- Discussions are here to kick-off the discussion and identify what the actionable item exactly is
- Issues are created later. Issues are well-formed, clear and actionable tasks

This pattern makes it easier for maintainers or contributors to find issues to work on since _almost every_ Issue is ready to be worked on.

## Submit bugs and feature ideas

Submit bugs to https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs
Submit feature ideas to https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas

Rules:
* Search for duplicates (in GitHub Issues and Discussions) before creating a new discussion
* Upvote for issues/discussions that you find useful

**Consider including in bug reports**

* `aerospace debug-windows` output, if the problem is about handling some windows
* Screenshots of problematic windows
* Videos of problematic windows
* What did you try to resolve the issue?
* Your config
* AeroSpace version
* macOS version

**Consider including in feature request**

* Use cases!
* Did I mention use cases?
* Alternative approaches
* Links to docs of similar features in other window managers that you know
* Synopsis, if you suggest a new command
* Mental model description

## Discuss issues/discussions

One of the most useful thing you can do is to discuss issues/discussions.

Imagine that you were assigned to fix the issue.
Try to suggest the best approach and design on how to fix the issue.
Suggest the synopsis/config format, reason in written form what is good about it, what is bad about it, what are the alternatives, etc.
Basically, see the "Prior discussion" section in [Submit Pull Requests](#submit-pull-requests).

If you have something to contribute to the conversation. Do it!

Please keep the conversation to the point. Discuss one issue at a time, crossreference other issues

You can take a look at the following issues:

* Most voted issues: https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
* Sometimes conversations happen on old issues that aren’t yet closed. See https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc
* Issues that are unclear on how to fix, or issues that require design of the interface (CLI or config interface) are tagged with `design-needed` tag https://github.com/nikitabobko/AeroSpace/issues?q=is%3Aissue+is%3Aopen+label%3Adesign-needed

## Submit Pull Requests

Small and trivial improvements can be submitted without any discussion.

**Prior discussion**. For non-trivial changes (such as user visible changes), it's always better to ask for prior approval and discuss what you want to do before doing it.

Please create a new discussion and describe you want to do.

Consider including

* What users will observe after your change?
* Feature interaction with existing features or potential future features
* What use cases does it cover
* What is the proposed syntax for the config
* What is the proposed synopsis of CLI command
* How you think it should be implemented (if you can describe it)
* etc.

Discussing that you want to do something doesn't put any obligations on you. If you don't want to start the discussion just because you're afraid that you won't do it. Don't be afraid!

**Commit hygiene**. Each submitted commit must be atomic change (a Pull Request may contain several commits). Don't introduce new functional changes together with refactorings in the same commit.

Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible and do not take this as an opportunity to do some "refactoring along the way".

A good commit message also mentions the motivation of the change (the commit describes what, why and how)

**License Agreement**. By contributing changes to this repository, you agree to license your contributions under the MIT license.

Maintainers can merge your pull request with arbitrary modifications.

**Pull request merge**. It cannot be guaranteed that your pull request will be merged even after the discussion.
Be ready that your pull request might be rejected because the implementation isn't good, or the approach is incorrect.

The prior discussion is here for you to minimize the risk of rejection.

## Spread the word

Do you like the project? Does AeroSpace finally fix your problems with windows management on macOS? Good to hear it!

* Spread the word in social networks! (Don't forget to share the link :) )
* Talk about AeroSpace to your colleagues and friends
* Write a blogpost about your workflows
* Record a YouTube video

## Share your workflow and tips

Submit your tips to [the Goodies page](https://nikitabobko.github.io/AeroSpace/goodies). The source code of the page can be found in `./docs` directory

## Support the project financially

Supporting the project financially counts as a contribution (even if it's just a $1/month). https://github.com/sponsors/nikitabobko


## /Gemfile

``` path="/Gemfile" 
# frozen_string_literal: true
ruby '~> 3.0' # >= 3.0 and < 4.0

source "https://rubygems.org"
gem 'asciidoctor', '2.0.23'
gem 'pygments.rb', '3.0'

```

## /LICENSE.txt

MIT License

Copyright (c) 2023 Nikita Bobko

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


## /Package.resolved

```resolved path="/Package.resolved" 
{
  "originHash" : "ce497cf9fcf14272fedb221cc48e1102e7d3c1bb2ab918cedfbfa9120f32fca3",
  "pins" : [
    {
      "identity" : "antlr4",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/antlr/antlr4",
      "state" : {
        "revision" : "7ed420ff2c78d62883875c442d75f32e73bc86c8",
        "version" : "4.13.1"
      }
    },
    {
      "identity" : "bluesocket",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Kitura/BlueSocket.git",
      "state" : {
        "revision" : "7b23a867008e0027bfd6f4d398d44720707bc8ca",
        "version" : "2.0.4"
      }
    },
    {
      "identity" : "hotkey",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/soffes/HotKey",
      "state" : {
        "revision" : "a3cf605d7a96f6ff50e04fcb6dea6e2613cfcbe4",
        "version" : "0.2.1"
      }
    },
    {
      "identity" : "issoundadditions",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/InerziaSoft/ISSoundAdditions",
      "state" : {
        "revision" : "4b555f0354e6c280917bae8a598a258efe87ab98",
        "version" : "2.0.1"
      }
    },
    {
      "identity" : "swift-collections",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-collections",
      "state" : {
        "revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
        "version" : "1.1.4"
      }
    },
    {
      "identity" : "tomlkit",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/LebJe/TOMLKit",
      "state" : {
        "revision" : "404c4dd011743461bff12d00a5118d0ed59d630c",
        "version" : "0.5.5"
      }
    }
  ],
  "version" : 3
}

```

## /Package.swift

```swift path="/Package.swift" 
// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AeroSpacePackage",
    // Runtime support for parameterized protocol types is only available in macOS 13.0.0 or newer
    // And it specifies deploymentTarget for CLI
    platforms: [.macOS(.v13)],
    // Products define the executables and libraries a package produces, making them visible to other packages.
    products: [
        .executable(name: "aerospace", targets: ["Cli"]),
        // Don't use this build for release, use xcode instead
        .executable(name: "AeroSpaceApp", targets: ["AeroSpaceApp"]),
        // We only need to expose this as a product for xcode
        .library(name: "AppBundle", targets: ["AppBundle"]),
    ],
    dependencies: [
        .package(path: "./ShellParserGenerated"),
        .package(url: "https://github.com/InerziaSoft/ISSoundAdditions", exact: "2.0.1"),
        .package(url: "https://github.com/Kitura/BlueSocket", exact: "2.0.4"),
        .package(url: "https://github.com/LebJe/TOMLKit", exact: "0.5.5"),
        .package(url: "https://github.com/apple/swift-collections", exact: "1.1.4"),
        .package(url: "https://github.com/soffes/HotKey", exact: "0.2.1"),
    ],
    // Targets are the basic building blocks of a package, defining a module or a test suite.
    // Targets can depend on other targets in this package and products from dependencies.
    targets: [
        // Exposes the private _AXUIElementGetWindow function to swift
        .target(
            name: "PrivateApi",
            path: "Sources/PrivateApi",
            publicHeadersPath: "include",
        ),
        .target(
            name: "Common",
            dependencies: [
                .product(name: "Collections", package: "swift-collections"),
            ],
        ),
        .target(
            name: "AppBundle",
            dependencies: [
                .product(name: "Collections", package: "swift-collections"),
                .product(name: "HotKey", package: "HotKey"),
                .product(name: "ISSoundAdditions", package: "ISSoundAdditions"),
                .product(name: "ShellParserGenerated", package: "ShellParserGenerated"),
                .product(name: "Socket", package: "BlueSocket"),
                .product(name: "TOMLKit", package: "TOMLKit"),
                .target(name: "Common"),
                .target(name: "PrivateApi"),
            ],
            swiftSettings: [
                .enableUpcomingFeature("NonisolatedNonsendingByDefault"),
            ],
        ),
        .executableTarget(
            name: "AeroSpaceApp",
            dependencies: [
                .target(name: "AppBundle"),
            ],
        ),
        .executableTarget(
            name: "Cli",
            dependencies: [
                .target(name: "Common"),
                .product(name: "Socket", package: "BlueSocket"),
            ],
        ),
        .testTarget(
            name: "AppBundleTests",
            dependencies: [
                .target(name: "AppBundle"),
            ],
        ),
    ],
)

```

## /README.md

# AeroSpace Beta [![Build](https://github.com/nikitabobko/AeroSpace/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/nikitabobko/AeroSpace/actions/workflows/build.yml)

<img src="./resources/Assets.xcassets/AppIcon.appiconset/icon.png" width="40%" align="right">

AeroSpace is an i3-like tiling window manager for macOS

Videos:
- [YouTube 91 sec Demo](https://www.youtube.com/watch?v=UOl7ErqWbrk)
- [YouTube Guide by Josean Martinez](https://www.youtube.com/watch?v=-FoWClVHG5g)

Docs:
- [AeroSpace Guide](https://nikitabobko.github.io/AeroSpace/guide)
- [AeroSpace Commands](https://nikitabobko.github.io/AeroSpace/commands)
- [AeroSpace Goodies](https://nikitabobko.github.io/AeroSpace/goodies)

## Project status

Public Beta. AeroSpace can be used as a daily driver, but expect breaking changes until 1.0 is reached.

What stops us from 1.0 release:
- [x] https://github.com/nikitabobko/AeroSpace/issues/131 Performance. Implement thread-per-application to circumvent macOS blocking AX API.
- [ ] https://github.com/nikitabobko/AeroSpace/issues/1215 _Big refactoring_. Rewrite mutable double-linked core tree data structure to immutable single-linked persistent tree.
  Important for: stability and potential performance
  - [ ] https://github.com/nikitabobko/AeroSpace/issues/1216 The big refactoring will help us to fix stability issue that windows may randomly jump to the focused workspace
  - [ ] https://github.com/nikitabobko/AeroSpace/issues/68 The big refactoring will help us to support macOS native tabs
- [ ] https://github.com/nikitabobko/AeroSpace/issues/278 Implement shell-like combinators.
  Ignore a lot of crazy fuss in the issue,
  We are most probably going with the minimal approach to only introduce common shell-combinators: `||`, `&&`, `;` and `eval` command to send multiple commands in one go.
- [ ] https://github.com/nikitabobko/AeroSpace/issues/1012 Investigate a possibility to use `CGEvent.tapCreate` API for global hotkeys
  - [ ] https://github.com/nikitabobko/AeroSpace/issues/28 Maybe it will allow to distinguish left and right modifiers. Maybe not

Big and important issues which will go after 1.0 release:
- [ ] https://github.com/nikitabobko/AeroSpace/issues/2 sticky windows
- [ ] https://github.com/nikitabobko/AeroSpace/issues/260 Dynamic TWM

## Key features

- Tiling window manager based on a [tree paradigm](https://nikitabobko.github.io/AeroSpace/guide#tree)
- [i3](https://i3wm.org/) inspired
- Fast workspaces switching without animations and without the necessity to disable SIP
- AeroSpace employs its [own emulation of virtual workspaces](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces) instead of relying on native macOS Spaces due to [their considerable limitations](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces)
- Plain text configuration (dotfiles friendly). See: [default-config.toml](https://nikitabobko.github.io/AeroSpace/guide#default-config)
- CLI first (manpages and shell completion included)
- Doesn't require disabling SIP (System Integrity Protection)
- [Proper multi-monitor support](https://nikitabobko.github.io/AeroSpace/guide#multiple-monitors) (i3-like paradigm)

## Installation

Install via [Homebrew](https://brew.sh/) to get autoupdates (Preferred)

```
brew install --cask nikitabobko/tap/aerospace
```

In multi-monitor setup please make sure that monitors [are properly arranged](https://nikitabobko.github.io/AeroSpace/guide#proper-monitor-arrangement).

Other installation options: https://nikitabobko.github.io/AeroSpace/guide#installation

> [!NOTE]
> By using AeroSpace, you acknowledge that it's not [notarized](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution).
>
> Notarization is a "security" feature by Apple.
> You send binaries to Apple, and they either approve them or not.
> In reality, notarization is about building binaries the way Apple likes it.
>
> I don't have anything against notarization as a concept.
> I specifically don't like the way Apple does notarization.
> I don't have time to deal with Apple.
>
> [Homebrew installation script](https://github.com/nikitabobko/homebrew-tap/blob/main/Casks/aerospace.rb) is configured to
> automatically delete `com.apple.quarantine` attribute, that's why the app should work out of the box, without any warnings that
> "Apple cannot check AeroSpace for malicious software"

## Community, discussions, issues

AeroSpace project doesn't accept Issues directly - we ask you to create a [Discussion](https://github.com/nikitabobko/AeroSpace/discussions) first.
Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.

Community discussions happen at GitHub Discussions.
There you can discuss bugs, propose new features, ask your questions, show off your setup, or just chat.

There are 7 channels:
-   [#all](https://github.com/nikitabobko/AeroSpace/discussions).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions.atom?discussions_q=sort%3Adate_created).
    Feed with all discussions.
-   [#announcements](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements.atom?discussions_q=category%3Aannouncements+sort%3Adate_created).
    Only maintainers can post here.
    Highly moderated traffic.
-   [#announcements-releases](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements-releases).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/announcements-releases.atom?discussions_q=category%3Aannouncements-releases+sort%3Adate_created).
    Announcements about non-patch releases.
    Only maintainers can post here.
-   [#feature-ideas](https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/feature-ideas.atom?discussions_q=category%3Afeature-ideas+sort%3Adate_created).
-   [#general](https://github.com/nikitabobko/AeroSpace/discussions/categories/general).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/general.atom?discussions_q=sort%3Adate_created+category%3Ageneral).
-   [#potential-bugs](https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs.atom?discussions_q=category%3Apotential-bugs+sort%3Adate_created).
    If you think that you have encountered a bug, you can discuss your bugs here.
-   [#questions-and-answers](https://github.com/nikitabobko/AeroSpace/discussions/categories/questions-and-answers).
    [RSS](https://github.com/nikitabobko/AeroSpace/discussions/categories/questions-and-answers.atom?discussions_q=category%3Aquestions-and-answers+sort%3Adate_created).
    Everyone is welcome to ask questions.
    Everyone is encouraged to answer other people's questions.

## Development

A notes on how to setup the project, build it, how to run the tests, etc. can be found here: [dev-docs/development.md](./dev-docs/development.md)

## Project values

**Values**
- AeroSpace is targeted at advanced users and developers
- Keyboard centric
- Breaking changes (configuration files, CLI, behavior) are avoided as much as possible, but it must not let the software stagnate.
  Thus breaking changes can happen, but with careful considerations and helpful message.
  [Semver](https://semver.org/) major version is bumped in case of a breaking change (It's all guaranteed once AeroSpace reaches 1.0 version, until then breaking changes just happen)
- AeroSpace doesn't use GUI, unless necessarily
  - AeroSpace will never provide a GUI for configuration.
    For advanced users, it's easier to edit a configuration file in text editor rather than navigating through checkboxes in GUI.
  - Status menu icon is ok, because visual feedback is needed
- Provide _practical_ features. Fancy appearance features are not _practical_ (e.g. window borders, transparency, animations, etc.)
- "dark magic" (aka "private APIs", "code injections", etc.) must be avoided as much as possible
  - Right now, AeroSpace uses only a single private API to get window ID of accessibility object `_AXUIElementGetWindow`.
    Everything else is [macOS public accessibility API](https://developer.apple.com/documentation/applicationservices/axuielement_h).
  - AeroSpace will never require you to disable SIP (System Integrity Protection).
  - The goal is to make AeroSpace easily maintainable, and resistant to macOS updates.

**Non Values**
- Play nicely with existing macOS features.
  If limitations are imposed then AeroSpace won't play nicely with existing macOS features
  (For example, AeroSpace doesn't acknowledge the existence of macOS Spaces, and it uses [emulation of its own workspaces](https://nikitabobko.github.io/AeroSpace/guide#emulation-of-virtual-workspaces))
- Ricing.
  AeroSpace provides only a very minimal support for ricing - gaps and a few callbacks for integrations with bars.
  The current maintainer doesn't care about ricing.
  Ricing issues are not a priority, and they are mostly ignored.
  The ricing stance can change only with the appearance of more maintainers.

## macOS compatibility table

|                                                                                | macOS 13 (Ventura) | macOS 14 (Sonoma) | macOS 15 (Sequoia) | macOS 26 (Tahoe) |
| ------------------------------------------------------------------------------ | ------------------ | ----------------- | ------------------ | ---------------- |
| AeroSpace binary runs on ...                                                   | +                  | +                 | +                  | +                |
| AeroSpace debug build from sources is supported on ...                         |                    | +                 | +                  | +                |
| AeroSpace release build from sources is supported on ... (Requires Xcode 26+)  |                    |                   | +                  | +                |

## Sponsorship

AeroSpace is developed and maintained in my free time.
If you find it useful, [consider sponsoring](https://github.com/sponsors/nikitabobko#sponsors).

## People who have write access

In alphabetical order:

- [@mobile-ar](https://github.com/mobile-ar/)
- [@nikitabobko](https://github.com/nikitabobko/)

## Tip of the day

```bash
defaults write -g NSWindowShouldDragOnGesture -bool true
```

Now, you can move windows by holding `ctrl`+`cmd` and dragging any part of the window (not necessarily the window title)

Source: [reddit](https://www.reddit.com/r/MacOS/comments/k6hiwk/keyboard_modifier_to_simplify_click_drag_of/)

## Related projects

- [Amethyst](https://github.com/ianyh/Amethyst)
- [yabai](https://github.com/koekeishiya/yabai)


## /ShellParserGenerated/.gitignore

```gitignore path="/ShellParserGenerated/.gitignore" 
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc

```

## /ShellParserGenerated/Package.swift

```swift path="/ShellParserGenerated/Package.swift" 
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "ShellParserGenerated",
    products: [
        // Products define the executables and libraries a package produces, making them visible to other packages.
        .library(
            name: "ShellParserGenerated",
            targets: ["ShellParserGenerated"]
        ),
    ],
    dependencies: [
        .package(url: "https://github.com/antlr/antlr4", exact: "4.13.1"),
    ],
    targets: [
        // Targets are the basic building blocks of a package, defining a module or a test suite.
        // Targets can depend on other targets in this package and products from dependencies.
        .target(
            name: "ShellParserGenerated",
            dependencies: [
                .product(name: "Antlr4Static", package: "antlr4"),
            ]
        ),
    ]
)

```

## /ShellParserGenerated/Sources/ShellParserGenerated/ShellLexer.swift

```swift path="/ShellParserGenerated/Sources/ShellParserGenerated/ShellLexer.swift" 
// Generated from ./grammar/ShellLexer.g4 by ANTLR 4.13.1
import Antlr4

open class ShellLexer: Lexer {

	internal static var _decisionToDFA: [DFA] = {
          var decisionToDFA = [DFA]()
          let length = ShellLexer._ATN.getNumberOfDecisions()
          for i in 0..<length {
          	    decisionToDFA.append(DFA(ShellLexer._ATN.getDecisionState(i)!, i))
          }
           return decisionToDFA
     }()

	internal static let _sharedContextCache = PredictionContextCache()

	public
	static let TRIPLE_QUOTE=1, SINGLE_QUOTED_STRING=2, LDQUOTE=3, LPAR=4, INTERPOLATION_START=5, 
            RPAR=6, ELIF=7, IF=8, SWITCH=9, CASE=10, DO=11, THEN=12, ELSE=13, 
            FOR=14, WHILE=15, CATCH=16, IN=17, END=18, DEFER=19, AND=20, 
            PIPE=21, OR=22, SEMICOLON=23, NL=24, WORD=25, ARG=26, ESCAPE_NEWLINE=27, 
            COMMENT=28, SPACES=29, ANY=30, TEXT=31, INTERPOLATION_START_IN_DSTRING=32, 
            ESCAPE_SEQUENCE=33, RDQUOTE=34

	public
	static let IN_DSTRING=1
	public
	static let channelNames: [String] = [
		"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
	]

	public
	static let modeNames: [String] = [
		"DEFAULT_MODE", "IN_DSTRING"
	]

	public
	static let ruleNames: [String] = [
		"TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START", 
		"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE", 
		"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL", 
		"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING", 
		"ESCAPE_SEQUENCE", "RDQUOTE"
	]

	private static let _LITERAL_NAMES: [String?] = [
		nil, nil, nil, nil, "'('", nil, "')'", nil, nil, nil, nil, nil, nil, nil, 
		nil, nil, nil, nil, nil, nil, "'&&'", "'|'", "'||'", "';'"
	]
	private static let _SYMBOLIC_NAMES: [String?] = [
		nil, "TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START", 
		"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE", 
		"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL", 
		"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING", 
		"ESCAPE_SEQUENCE", "RDQUOTE"
	]
	public
	static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)


	override open
	func getVocabulary() -> Vocabulary {
		return ShellLexer.VOCABULARY
	}

	public
	required init(_ input: CharStream) {
	    RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION)
		super.init(input)
		_interp = LexerATNSimulator(self, ShellLexer._ATN, ShellLexer._decisionToDFA, ShellLexer._sharedContextCache)
	}

	override open
	func getGrammarFileName() -> String { return "ShellLexer.g4" }

	override open
	func getRuleNames() -> [String] { return ShellLexer.ruleNames }

	override open
	func getSerializedATN() -> [Int] { return ShellLexer._serializedATN }

	override open
	func getChannelNames() -> [String] { return ShellLexer.channelNames }

	override open
	func getModeNames() -> [String] { return ShellLexer.modeNames }

	override open
	func getATN() -> ATN { return ShellLexer._ATN }

	override open
	func action(_ _localctx: RuleContext?,  _ ruleIndex: Int, _ actionIndex: Int) throws {
		switch (ruleIndex) {
		case 5:
			try RPAR_action((_localctx as RuleContext?), actionIndex)

		case 33:
			try RDQUOTE_action((_localctx as RuleContext?), actionIndex)

		default: break
		}
	}
	private func RPAR_action(_ _localctx: RuleContext?,  _ actionIndex: Int) throws {
		switch (actionIndex) {
		case 0:

			    _ = try? popMode()


		 default: break
		}
	}
	private func RDQUOTE_action(_ _localctx: RuleContext?,  _ actionIndex: Int) throws {
		switch (actionIndex) {
		case 1:

			    _ = try? popMode()


		 default: break
		}
	}

	static let _serializedATN:[Int] = [
		4,0,34,316,6,-1,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,
		6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,
		2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,
		2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,
		2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33,7,33,1,0,1,0,1,
		0,1,0,1,0,1,0,3,0,77,8,0,1,1,1,1,5,1,81,8,1,10,1,12,1,84,9,1,1,1,1,1,1,
		2,1,2,1,2,1,2,1,3,1,3,1,3,1,3,1,4,1,4,1,4,1,4,1,4,1,5,1,5,1,5,1,6,1,6,
		1,6,1,6,1,6,1,6,5,6,110,8,6,10,6,12,6,113,9,6,1,7,1,7,1,7,1,7,5,7,119,
		8,7,10,7,12,7,122,9,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,5,8,132,8,8,10,8,
		12,8,135,9,8,1,9,1,9,1,9,1,9,1,9,1,9,5,9,143,8,9,10,9,12,9,146,9,9,1,10,
		1,10,1,10,1,10,5,10,152,8,10,10,10,12,10,155,9,10,1,11,1,11,1,11,1,11,
		1,11,1,11,5,11,163,8,11,10,11,12,11,166,9,11,1,12,1,12,1,12,1,12,1,12,
		1,12,5,12,174,8,12,10,12,12,12,177,9,12,1,13,1,13,1,13,1,13,1,13,5,13,
		184,8,13,10,13,12,13,187,9,13,1,14,1,14,1,14,1,14,1,14,1,14,1,14,5,14,
		196,8,14,10,14,12,14,199,9,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,5,15,
		208,8,15,10,15,12,15,211,9,15,1,16,1,16,1,16,1,16,5,16,217,8,16,10,16,
		12,16,220,9,16,1,17,1,17,1,17,1,17,1,17,5,17,227,8,17,10,17,12,17,230,
		9,17,1,18,1,18,1,18,1,18,1,18,1,18,1,18,5,18,239,8,18,10,18,12,18,242,
		9,18,1,19,1,19,1,19,1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,23,3,23,255,8,
		23,1,23,3,23,258,8,23,1,23,1,23,1,24,4,24,263,8,24,11,24,12,24,264,1,25,
		4,25,268,8,25,11,25,12,25,269,1,26,1,26,3,26,274,8,26,1,26,3,26,277,8,
		26,1,26,1,26,1,26,1,26,1,27,1,27,5,27,285,8,27,10,27,12,27,288,9,27,1,
		27,1,27,1,28,4,28,293,8,28,11,28,12,28,294,1,28,1,28,1,29,1,29,1,30,4,
		30,302,8,30,11,30,12,30,303,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,
		33,1,33,1,33,1,82,0,34,2,1,4,2,6,3,8,4,10,5,12,6,14,7,16,8,18,9,20,10,
		22,11,24,12,26,13,28,14,30,15,32,16,34,17,36,18,38,19,40,20,42,21,44,22,
		46,23,48,24,50,25,52,26,54,27,56,28,58,29,60,30,62,31,64,32,66,33,68,34,
		2,0,1,5,4,0,45,47,65,90,95,95,97,122,8,0,33,33,37,37,43,57,61,61,65,90,
		94,95,97,123,125,125,1,0,10,10,2,0,9,9,32,32,3,0,34,34,36,36,92,92,338,
		0,2,1,0,0,0,0,4,1,0,0,0,0,6,1,0,0,0,0,8,1,0,0,0,0,10,1,0,0,0,0,12,1,0,
		0,0,0,14,1,0,0,0,0,16,1,0,0,0,0,18,1,0,0,0,0,20,1,0,0,0,0,22,1,0,0,0,0,
		24,1,0,0,0,0,26,1,0,0,0,0,28,1,0,0,0,0,30,1,0,0,0,0,32,1,0,0,0,0,34,1,
		0,0,0,0,36,1,0,0,0,0,38,1,0,0,0,0,40,1,0,0,0,0,42,1,0,0,0,0,44,1,0,0,0,
		0,46,1,0,0,0,0,48,1,0,0,0,0,50,1,0,0,0,0,52,1,0,0,0,0,54,1,0,0,0,0,56,
		1,0,0,0,0,58,1,0,0,0,0,60,1,0,0,0,1,62,1,0,0,0,1,64,1,0,0,0,1,66,1,0,0,
		0,1,68,1,0,0,0,2,76,1,0,0,0,4,78,1,0,0,0,6,87,1,0,0,0,8,91,1,0,0,0,10,
		95,1,0,0,0,12,100,1,0,0,0,14,103,1,0,0,0,16,114,1,0,0,0,18,123,1,0,0,0,
		20,136,1,0,0,0,22,147,1,0,0,0,24,156,1,0,0,0,26,167,1,0,0,0,28,178,1,0,
		0,0,30,188,1,0,0,0,32,200,1,0,0,0,34,212,1,0,0,0,36,221,1,0,0,0,38,231,
		1,0,0,0,40,243,1,0,0,0,42,246,1,0,0,0,44,248,1,0,0,0,46,251,1,0,0,0,48,
		254,1,0,0,0,50,262,1,0,0,0,52,267,1,0,0,0,54,271,1,0,0,0,56,282,1,0,0,
		0,58,292,1,0,0,0,60,298,1,0,0,0,62,301,1,0,0,0,64,305,1,0,0,0,66,310,1,
		0,0,0,68,313,1,0,0,0,70,71,5,34,0,0,71,72,5,34,0,0,72,77,5,34,0,0,73,74,
		5,39,0,0,74,75,5,39,0,0,75,77,5,39,0,0,76,70,1,0,0,0,76,73,1,0,0,0,77,
		3,1,0,0,0,78,82,5,39,0,0,79,81,9,0,0,0,80,79,1,0,0,0,81,84,1,0,0,0,82,
		83,1,0,0,0,82,80,1,0,0,0,83,85,1,0,0,0,84,82,1,0,0,0,85,86,5,39,0,0,86,
		5,1,0,0,0,87,88,5,34,0,0,88,89,1,0,0,0,89,90,6,2,0,0,90,7,1,0,0,0,91,92,
		5,40,0,0,92,93,1,0,0,0,93,94,6,3,1,0,94,9,1,0,0,0,95,96,5,36,0,0,96,97,
		5,40,0,0,97,98,1,0,0,0,98,99,6,4,1,0,99,11,1,0,0,0,100,101,5,41,0,0,101,
		102,6,5,2,0,102,13,1,0,0,0,103,104,5,101,0,0,104,105,5,108,0,0,105,106,
		5,105,0,0,106,107,5,102,0,0,107,111,1,0,0,0,108,110,3,48,23,0,109,108,
		1,0,0,0,110,113,1,0,0,0,111,109,1,0,0,0,111,112,1,0,0,0,112,15,1,0,0,0,
		113,111,1,0,0,0,114,115,5,105,0,0,115,116,5,102,0,0,116,120,1,0,0,0,117,
		119,3,48,23,0,118,117,1,0,0,0,119,122,1,0,0,0,120,118,1,0,0,0,120,121,
		1,0,0,0,121,17,1,0,0,0,122,120,1,0,0,0,123,124,5,115,0,0,124,125,5,119,
		0,0,125,126,5,105,0,0,126,127,5,116,0,0,127,128,5,99,0,0,128,129,5,104,
		0,0,129,133,1,0,0,0,130,132,3,48,23,0,131,130,1,0,0,0,132,135,1,0,0,0,
		133,131,1,0,0,0,133,134,1,0,0,0,134,19,1,0,0,0,135,133,1,0,0,0,136,137,
		5,99,0,0,137,138,5,97,0,0,138,139,5,115,0,0,139,140,5,101,0,0,140,144,
		1,0,0,0,141,143,3,48,23,0,142,141,1,0,0,0,143,146,1,0,0,0,144,142,1,0,
		0,0,144,145,1,0,0,0,145,21,1,0,0,0,146,144,1,0,0,0,147,148,5,100,0,0,148,
		149,5,111,0,0,149,153,1,0,0,0,150,152,3,48,23,0,151,150,1,0,0,0,152,155,
		1,0,0,0,153,151,1,0,0,0,153,154,1,0,0,0,154,23,1,0,0,0,155,153,1,0,0,0,
		156,157,5,116,0,0,157,158,5,104,0,0,158,159,5,101,0,0,159,160,5,110,0,
		0,160,164,1,0,0,0,161,163,3,48,23,0,162,161,1,0,0,0,163,166,1,0,0,0,164,
		162,1,0,0,0,164,165,1,0,0,0,165,25,1,0,0,0,166,164,1,0,0,0,167,168,5,101,
		0,0,168,169,5,108,0,0,169,170,5,115,0,0,170,171,5,101,0,0,171,175,1,0,
		0,0,172,174,3,48,23,0,173,172,1,0,0,0,174,177,1,0,0,0,175,173,1,0,0,0,
		175,176,1,0,0,0,176,27,1,0,0,0,177,175,1,0,0,0,178,179,5,102,0,0,179,180,
		5,111,0,0,180,181,5,114,0,0,181,185,1,0,0,0,182,184,3,48,23,0,183,182,
		1,0,0,0,184,187,1,0,0,0,185,183,1,0,0,0,185,186,1,0,0,0,186,29,1,0,0,0,
		187,185,1,0,0,0,188,189,5,119,0,0,189,190,5,104,0,0,190,191,5,105,0,0,
		191,192,5,108,0,0,192,193,5,101,0,0,193,197,1,0,0,0,194,196,3,48,23,0,
		195,194,1,0,0,0,196,199,1,0,0,0,197,195,1,0,0,0,197,198,1,0,0,0,198,31,
		1,0,0,0,199,197,1,0,0,0,200,201,5,99,0,0,201,202,5,97,0,0,202,203,5,116,
		0,0,203,204,5,99,0,0,204,205,5,104,0,0,205,209,1,0,0,0,206,208,3,48,23,
		0,207,206,1,0,0,0,208,211,1,0,0,0,209,207,1,0,0,0,209,210,1,0,0,0,210,
		33,1,0,0,0,211,209,1,0,0,0,212,213,5,105,0,0,213,214,5,110,0,0,214,218,
		1,0,0,0,215,217,3,48,23,0,216,215,1,0,0,0,217,220,1,0,0,0,218,216,1,0,
		0,0,218,219,1,0,0,0,219,35,1,0,0,0,220,218,1,0,0,0,221,222,5,101,0,0,222,
		223,5,110,0,0,223,224,5,100,0,0,224,228,1,0,0,0,225,227,3,48,23,0,226,
		225,1,0,0,0,227,230,1,0,0,0,228,226,1,0,0,0,228,229,1,0,0,0,229,37,1,0,
		0,0,230,228,1,0,0,0,231,232,5,100,0,0,232,233,5,101,0,0,233,234,5,102,
		0,0,234,235,5,101,0,0,235,236,5,114,0,0,236,240,1,0,0,0,237,239,3,48,23,
		0,238,237,1,0,0,0,239,242,1,0,0,0,240,238,1,0,0,0,240,241,1,0,0,0,241,
		39,1,0,0,0,242,240,1,0,0,0,243,244,5,38,0,0,244,245,5,38,0,0,245,41,1,
		0,0,0,246,247,5,124,0,0,247,43,1,0,0,0,248,249,5,124,0,0,249,250,5,124,
		0,0,250,45,1,0,0,0,251,252,5,59,0,0,252,47,1,0,0,0,253,255,3,58,28,0,254,
		253,1,0,0,0,254,255,1,0,0,0,255,257,1,0,0,0,256,258,5,13,0,0,257,256,1,
		0,0,0,257,258,1,0,0,0,258,259,1,0,0,0,259,260,5,10,0,0,260,49,1,0,0,0,
		261,263,7,0,0,0,262,261,1,0,0,0,263,264,1,0,0,0,264,262,1,0,0,0,264,265,
		1,0,0,0,265,51,1,0,0,0,266,268,7,1,0,0,267,266,1,0,0,0,268,269,1,0,0,0,
		269,267,1,0,0,0,269,270,1,0,0,0,270,53,1,0,0,0,271,273,5,92,0,0,272,274,
		3,58,28,0,273,272,1,0,0,0,273,274,1,0,0,0,274,276,1,0,0,0,275,277,3,56,
		27,0,276,275,1,0,0,0,276,277,1,0,0,0,277,278,1,0,0,0,278,279,5,10,0,0,
		279,280,1,0,0,0,280,281,6,26,3,0,281,55,1,0,0,0,282,286,5,35,0,0,283,285,
		8,2,0,0,284,283,1,0,0,0,285,288,1,0,0,0,286,284,1,0,0,0,286,287,1,0,0,
		0,287,289,1,0,0,0,288,286,1,0,0,0,289,290,6,27,3,0,290,57,1,0,0,0,291,
		293,7,3,0,0,292,291,1,0,0,0,293,294,1,0,0,0,294,292,1,0,0,0,294,295,1,
		0,0,0,295,296,1,0,0,0,296,297,6,28,3,0,297,59,1,0,0,0,298,299,9,0,0,0,
		299,61,1,0,0,0,300,302,8,4,0,0,301,300,1,0,0,0,302,303,1,0,0,0,303,301,
		1,0,0,0,303,304,1,0,0,0,304,63,1,0,0,0,305,306,5,36,0,0,306,307,5,40,0,
		0,307,308,1,0,0,0,308,309,6,31,1,0,309,65,1,0,0,0,310,311,5,92,0,0,311,
		312,9,0,0,0,312,67,1,0,0,0,313,314,5,34,0,0,314,315,6,33,4,0,315,69,1,
		0,0,0,28,0,1,76,82,111,120,133,144,153,164,175,185,197,209,218,228,240,
		254,257,262,264,267,269,273,276,286,294,303,5,5,1,0,5,0,0,1,5,0,6,0,0,
		1,33,1
	]

	public
	static let _ATN: ATN = try! ATNDeserializer().deserialize(_serializedATN)
}
```

## /ShellParserGenerated/Sources/ShellParserGenerated/ShellParser.swift

```swift path="/ShellParserGenerated/Sources/ShellParserGenerated/ShellParser.swift" 
// Generated from ./grammar/ShellParser.g4 by ANTLR 4.13.1
import Antlr4

open class ShellParser: Parser {

	internal static var _decisionToDFA: [DFA] = {
          var decisionToDFA = [DFA]()
          let length = ShellParser._ATN.getNumberOfDecisions()
          for i in 0..<length {
            decisionToDFA.append(DFA(ShellParser._ATN.getDecisionState(i)!, i))
           }
           return decisionToDFA
     }()

	internal static let _sharedContextCache = PredictionContextCache()

	public
	enum Tokens: Int {
		case EOF = -1, TRIPLE_QUOTE = 1, SINGLE_QUOTED_STRING = 2, LDQUOTE = 3, 
                 LPAR = 4, INTERPOLATION_START = 5, RPAR = 6, ELIF = 7, 
                 IF = 8, SWITCH = 9, CASE = 10, DO = 11, THEN = 12, ELSE = 13, 
                 FOR = 14, WHILE = 15, CATCH = 16, IN = 17, END = 18, DEFER = 19, 
                 AND = 20, PIPE = 21, OR = 22, SEMICOLON = 23, NL = 24, 
                 WORD = 25, ARG = 26, ESCAPE_NEWLINE = 27, COMMENT = 28, 
                 SPACES = 29, ANY = 30, TEXT = 31, INTERPOLATION_START_IN_DSTRING = 32, 
                 ESCAPE_SEQUENCE = 33, RDQUOTE = 34
	}

	public
	static let RULE_root = 0, RULE_cmds = 1, RULE_cmd = 2, RULE_arg = 3, RULE_dStringFragment = 4

	public
	static let ruleNames: [String] = [
		"root", "cmds", "cmd", "arg", "dStringFragment"
	]

	private static let _LITERAL_NAMES: [String?] = [
		nil, nil, nil, nil, "'('", nil, "')'", nil, nil, nil, nil, nil, nil, nil, 
		nil, nil, nil, nil, nil, nil, "'&&'", "'|'", "'||'", "';'"
	]
	private static let _SYMBOLIC_NAMES: [String?] = [
		nil, "TRIPLE_QUOTE", "SINGLE_QUOTED_STRING", "LDQUOTE", "LPAR", "INTERPOLATION_START", 
		"RPAR", "ELIF", "IF", "SWITCH", "CASE", "DO", "THEN", "ELSE", "FOR", "WHILE", 
		"CATCH", "IN", "END", "DEFER", "AND", "PIPE", "OR", "SEMICOLON", "NL", 
		"WORD", "ARG", "ESCAPE_NEWLINE", "COMMENT", "SPACES", "ANY", "TEXT", "INTERPOLATION_START_IN_DSTRING", 
		"ESCAPE_SEQUENCE", "RDQUOTE"
	]
	public
	static let VOCABULARY = Vocabulary(_LITERAL_NAMES, _SYMBOLIC_NAMES)

	override open
	func getGrammarFileName() -> String { return "ShellParser.g4" }

	override open
	func getRuleNames() -> [String] { return ShellParser.ruleNames }

	override open
	func getSerializedATN() -> [Int] { return ShellParser._serializedATN }

	override open
	func getATN() -> ATN { return ShellParser._ATN }


	override open
	func getVocabulary() -> Vocabulary {
	    return ShellParser.VOCABULARY
	}

	override public
	init(_ input:TokenStream) throws {
	    RuntimeMetaData.checkVersion("4.13.1", RuntimeMetaData.VERSION)
		try super.init(input)
		_interp = ParserATNSimulator(self,ShellParser._ATN,ShellParser._decisionToDFA, ShellParser._sharedContextCache)
	}


	public class RootContext: ParserRuleContext {
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func EOF() -> TerminalNode? {
				return getToken(ShellParser.Tokens.EOF.rawValue, 0)
			}
			open
			func NL() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.NL.rawValue)
			}
			open
			func NL(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.NL.rawValue, i)
			}
		override open
		func getRuleIndex() -> Int {
			return ShellParser.RULE_root
		}
	}
	@discardableResult
	 open func root() throws -> RootContext {
		var _localctx: RootContext
		_localctx = RootContext(_ctx, getState())
		try enterRule(_localctx, 0, ShellParser.RULE_root)
		var _la: Int = 0
		defer {
	    		try! exitRule()
	    }
		do {
		 	try enterOuterAlt(_localctx, 1)
		 	setState(13)
		 	try _errHandler.sync(self)
		 	_la = try _input.LA(1)
		 	while (_la == ShellParser.Tokens.NL.rawValue) {
		 		setState(10)
		 		try match(ShellParser.Tokens.NL.rawValue)


		 		setState(15)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 	}
		 	setState(20)
		 	try _errHandler.sync(self)
		 	switch (ShellParser.Tokens(rawValue: try _input.LA(1))!) {
		 	case .LPAR:fallthrough
		 	case .IF:fallthrough
		 	case .WORD:
		 		setState(16)
		 		try cmds()
		 		setState(17)
		 		try match(ShellParser.Tokens.EOF.rawValue)

		 		break

		 	case .EOF:
		 		setState(19)
		 		try match(ShellParser.Tokens.EOF.rawValue)

		 		break
		 	default:
		 		throw ANTLRException.recognition(e: NoViableAltException(self))
		 	}

		}
		catch ANTLRException.recognition(let re) {
			_localctx.exception = re
			_errHandler.reportError(self, re)
			try _errHandler.recover(self, re)
		}

		return _localctx
	}

	public class CmdsContext: ParserRuleContext {
		override open
		func getRuleIndex() -> Int {
			return ShellParser.RULE_cmds
		}
	}
	public class IfElseContext: CmdsContext {
			open
			func IF() -> TerminalNode? {
				return getToken(ShellParser.Tokens.IF.rawValue, 0)
			}
			open
			func cmd() -> [CmdContext] {
				return getRuleContexts(CmdContext.self)
			}
			open
			func cmd(_ i: Int) -> CmdContext? {
				return getRuleContext(CmdContext.self, i)
			}
			open
			func THEN() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.THEN.rawValue)
			}
			open
			func THEN(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.THEN.rawValue, i)
			}
			open
			func END() -> TerminalNode? {
				return getToken(ShellParser.Tokens.END.rawValue, 0)
			}
			open
			func cmds() -> [CmdsContext] {
				return getRuleContexts(CmdsContext.self)
			}
			open
			func cmds(_ i: Int) -> CmdsContext? {
				return getRuleContext(CmdsContext.self, i)
			}
			open
			func ELIF() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.ELIF.rawValue)
			}
			open
			func ELIF(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.ELIF.rawValue, i)
			}
			open
			func ELSE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.ELSE.rawValue, 0)
			}

		public
		init(_ ctx: CmdsContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class SeqContext: CmdsContext {
			open
			func cmd() -> CmdContext? {
				return getRuleContext(CmdContext.self, 0)
			}
			open
			func cmds() -> [CmdsContext] {
				return getRuleContexts(CmdsContext.self)
			}
			open
			func cmds(_ i: Int) -> CmdsContext? {
				return getRuleContext(CmdsContext.self, i)
			}
			open
			func SEMICOLON() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.SEMICOLON.rawValue)
			}
			open
			func SEMICOLON(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.SEMICOLON.rawValue, i)
			}
			open
			func NL() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.NL.rawValue)
			}
			open
			func NL(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.NL.rawValue, i)
			}

		public
		init(_ ctx: CmdsContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	@discardableResult
	 open func cmds() throws -> CmdsContext {
		var _localctx: CmdsContext
		_localctx = CmdsContext(_ctx, getState())
		try enterRule(_localctx, 2, ShellParser.RULE_cmds)
		var _la: Int = 0
		defer {
	    		try! exitRule()
	    }
		do {
			var _alt:Int
		 	setState(60)
		 	try _errHandler.sync(self)
		 	switch(try getInterpreter().adaptivePredict(_input,9, _ctx)) {
		 	case 1:
		 		_localctx =  IfElseContext(_localctx);
		 		try enterOuterAlt(_localctx, 1)
		 		setState(22)
		 		try match(ShellParser.Tokens.IF.rawValue)
		 		setState(23)
		 		try cmd(0)
		 		setState(24)
		 		try match(ShellParser.Tokens.THEN.rawValue)
		 		setState(26)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 		if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
		 			setState(25)
		 			try cmds()

		 		}

		 		setState(36)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 		while (_la == ShellParser.Tokens.ELIF.rawValue) {
		 			setState(28)
		 			try match(ShellParser.Tokens.ELIF.rawValue)
		 			setState(29)
		 			try cmd(0)
		 			setState(30)
		 			try match(ShellParser.Tokens.THEN.rawValue)
		 			setState(32)
		 			try _errHandler.sync(self)
		 			_la = try _input.LA(1)
		 			if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
		 				setState(31)
		 				try cmds()

		 			}



		 			setState(38)
		 			try _errHandler.sync(self)
		 			_la = try _input.LA(1)
		 		}
		 		setState(43)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 		if (_la == ShellParser.Tokens.ELSE.rawValue) {
		 			setState(39)
		 			try match(ShellParser.Tokens.ELSE.rawValue)
		 			setState(41)
		 			try _errHandler.sync(self)
		 			_la = try _input.LA(1)
		 			if (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 33554704) != 0)) {
		 				setState(40)
		 				try cmds()

		 			}


		 		}

		 		setState(45)
		 		try match(ShellParser.Tokens.END.rawValue)

		 		break
		 	case 2:
		 		_localctx =  SeqContext(_localctx);
		 		try enterOuterAlt(_localctx, 2)
		 		setState(47)
		 		try cmd(0)
		 		setState(49); 
		 		try _errHandler.sync(self)
		 		_alt = 1;
		 		repeat {
		 			switch (_alt) {
		 			case 1:
		 				setState(48)
		 				_la = try _input.LA(1)
		 				if (!(_la == ShellParser.Tokens.SEMICOLON.rawValue || _la == ShellParser.Tokens.NL.rawValue)) {
		 				try _errHandler.recoverInline(self)
		 				}
		 				else {
		 					_errHandler.reportMatch(self)
		 					try consume()
		 				}


		 				break
		 			default:
		 				throw ANTLRException.recognition(e: NoViableAltException(self))
		 			}
		 			setState(51); 
		 			try _errHandler.sync(self)
		 			_alt = try getInterpreter().adaptivePredict(_input,7,_ctx)
		 		} while (_alt != 2 && _alt !=  ATN.INVALID_ALT_NUMBER)
		 		setState(56)
		 		try _errHandler.sync(self)
		 		_alt = try getInterpreter().adaptivePredict(_input,8,_ctx)
		 		while (_alt != 1 && _alt != ATN.INVALID_ALT_NUMBER) {
		 			if ( _alt==1+1 ) {
		 				setState(53)
		 				try cmds()

		 		 
		 			}
		 			setState(58)
		 			try _errHandler.sync(self)
		 			_alt = try getInterpreter().adaptivePredict(_input,8,_ctx)
		 		}

		 		break
		 	case 3:
		 		_localctx =  SeqContext(_localctx);
		 		try enterOuterAlt(_localctx, 3)
		 		setState(59)
		 		try cmd(0)

		 		break
		 	default: break
		 	}
		}
		catch ANTLRException.recognition(let re) {
			_localctx.exception = re
			_errHandler.reportError(self, re)
			try _errHandler.recover(self, re)
		}

		return _localctx
	}


	public class CmdContext: ParserRuleContext {
		override open
		func getRuleIndex() -> Int {
			return ShellParser.RULE_cmd
		}
	}
	public class ArgsContext: CmdContext {
			open
			func WORD() -> TerminalNode? {
				return getToken(ShellParser.Tokens.WORD.rawValue, 0)
			}
			open
			func arg() -> [ArgContext] {
				return getRuleContexts(ArgContext.self)
			}
			open
			func arg(_ i: Int) -> ArgContext? {
				return getRuleContext(ArgContext.self, i)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class CmdErrorContext: CmdContext {
			open
			func LPAR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.LPAR.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.RPAR.rawValue)
			}
			open
			func RPAR(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, i)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class OrContext: CmdContext {
			open
			func cmd() -> [CmdContext] {
				return getRuleContexts(CmdContext.self)
			}
			open
			func cmd(_ i: Int) -> CmdContext? {
				return getRuleContext(CmdContext.self, i)
			}
			open
			func OR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.OR.rawValue, 0)
			}
			open
			func NL() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.NL.rawValue)
			}
			open
			func NL(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.NL.rawValue, i)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class ParensContext: CmdContext {
			open
			func LPAR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.LPAR.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class AndContext: CmdContext {
			open
			func cmd() -> [CmdContext] {
				return getRuleContexts(CmdContext.self)
			}
			open
			func cmd(_ i: Int) -> CmdContext? {
				return getRuleContext(CmdContext.self, i)
			}
			open
			func AND() -> TerminalNode? {
				return getToken(ShellParser.Tokens.AND.rawValue, 0)
			}
			open
			func NL() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.NL.rawValue)
			}
			open
			func NL(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.NL.rawValue, i)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class PipeContext: CmdContext {
			open
			func cmd() -> [CmdContext] {
				return getRuleContexts(CmdContext.self)
			}
			open
			func cmd(_ i: Int) -> CmdContext? {
				return getRuleContext(CmdContext.self, i)
			}
			open
			func PIPE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.PIPE.rawValue, 0)
			}
			open
			func NL() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.NL.rawValue)
			}
			open
			func NL(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.NL.rawValue, i)
			}

		public
		init(_ ctx: CmdContext) {
			super.init()
			copyFrom(ctx)
		}
	}

	 public final  func cmd( ) throws -> CmdContext   {
		return try cmd(0)
	}
	@discardableResult
	private func cmd(_ _p: Int) throws -> CmdContext   {
		let _parentctx: ParserRuleContext? = _ctx
		let _parentState: Int = getState()
		var _localctx: CmdContext
		_localctx = CmdContext(_ctx, _parentState)
		let _startState: Int = 4
		try enterRecursionRule(_localctx, 4, ShellParser.RULE_cmd, _p)
		var _la: Int = 0
		defer {
	    		try! unrollRecursionContexts(_parentctx)
	    }
		do {
			var _alt: Int
			try enterOuterAlt(_localctx, 1)
			setState(84)
			try _errHandler.sync(self)
			switch(try getInterpreter().adaptivePredict(_input,11, _ctx)) {
			case 1:
				_localctx = ArgsContext(_localctx)
				_ctx = _localctx

				setState(63)
				try match(ShellParser.Tokens.WORD.rawValue)
				setState(67)
				try _errHandler.sync(self)
				_alt = try getInterpreter().adaptivePredict(_input,10,_ctx)
				while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
					if ( _alt==1 ) {
						setState(64)
						try arg()

				 
					}
					setState(69)
					try _errHandler.sync(self)
					_alt = try getInterpreter().adaptivePredict(_input,10,_ctx)
				}

				break
			case 2:
				_localctx = ParensContext(_localctx)
				_ctx = _localctx
				setState(70)
				try match(ShellParser.Tokens.LPAR.rawValue)
				setState(71)
				try cmds()
				setState(72)
				try match(ShellParser.Tokens.RPAR.rawValue)

				break
			case 3:
				_localctx = CmdErrorContext(_localctx)
				_ctx = _localctx
				setState(74)
				try match(ShellParser.Tokens.LPAR.rawValue)
				setState(75)
				try cmds()
				setState(76)
				try match(ShellParser.Tokens.RPAR.rawValue)
				setState(77)
				try match(ShellParser.Tokens.RPAR.rawValue)
				notifyErrorListeners("Unbalanced parenthesis")

				break
			case 4:
				_localctx = CmdErrorContext(_localctx)
				_ctx = _localctx
				setState(80)
				try match(ShellParser.Tokens.LPAR.rawValue)
				setState(81)
				try cmds()
				notifyErrorListeners("Unbalanced parenthesis")

				break
			default: break
			}
			_ctx!.stop = try _input.LT(-1)
			setState(115)
			try _errHandler.sync(self)
			_alt = try getInterpreter().adaptivePredict(_input,16,_ctx)
			while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
				if ( _alt==1 ) {
					if _parseListeners != nil {
					   try triggerExitRuleEvent()
					}
					setState(113)
					try _errHandler.sync(self)
					switch(try getInterpreter().adaptivePredict(_input,15, _ctx)) {
					case 1:
						_localctx = PipeContext(  CmdContext(_parentctx, _parentState))
						try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
						setState(86)
						if (!(precpred(_ctx, 6))) {
						    throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 6)"))
						}
						setState(90)
						try _errHandler.sync(self)
						_la = try _input.LA(1)
						while (_la == ShellParser.Tokens.NL.rawValue) {
							setState(87)
							try match(ShellParser.Tokens.NL.rawValue)


							setState(92)
							try _errHandler.sync(self)
							_la = try _input.LA(1)
						}
						setState(93)
						try match(ShellParser.Tokens.PIPE.rawValue)
						setState(94)
						try cmd(7)

						break
					case 2:
						_localctx = AndContext(  CmdContext(_parentctx, _parentState))
						try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
						setState(95)
						if (!(precpred(_ctx, 5))) {
						    throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 5)"))
						}
						setState(99)
						try _errHandler.sync(self)
						_la = try _input.LA(1)
						while (_la == ShellParser.Tokens.NL.rawValue) {
							setState(96)
							try match(ShellParser.Tokens.NL.rawValue)


							setState(101)
							try _errHandler.sync(self)
							_la = try _input.LA(1)
						}
						setState(102)
						try match(ShellParser.Tokens.AND.rawValue)
						setState(103)
						try cmd(6)

						break
					case 3:
						_localctx = OrContext(  CmdContext(_parentctx, _parentState))
						try pushNewRecursionContext(_localctx, _startState, ShellParser.RULE_cmd)
						setState(104)
						if (!(precpred(_ctx, 4))) {
						    throw ANTLRException.recognition(e:FailedPredicateException(self, "precpred(_ctx, 4)"))
						}
						setState(108)
						try _errHandler.sync(self)
						_la = try _input.LA(1)
						while (_la == ShellParser.Tokens.NL.rawValue) {
							setState(105)
							try match(ShellParser.Tokens.NL.rawValue)


							setState(110)
							try _errHandler.sync(self)
							_la = try _input.LA(1)
						}
						setState(111)
						try match(ShellParser.Tokens.OR.rawValue)
						setState(112)
						try cmd(5)

						break
					default: break
					}
			 
				}
				setState(117)
				try _errHandler.sync(self)
				_alt = try getInterpreter().adaptivePredict(_input,16,_ctx)
			}

		}
		catch ANTLRException.recognition(let re) {
			_localctx.exception = re
			_errHandler.reportError(self, re)
			try _errHandler.recover(self, re)
		}

		return _localctx;
	}

	public class ArgContext: ParserRuleContext {
		override open
		func getRuleIndex() -> Int {
			return ShellParser.RULE_arg
		}
	}
	public class WordContext: ArgContext {
			open
			func ARG() -> TerminalNode? {
				return getToken(ShellParser.Tokens.ARG.rawValue, 0)
			}
			open
			func WORD() -> TerminalNode? {
				return getToken(ShellParser.Tokens.WORD.rawValue, 0)
			}

		public
		init(_ ctx: ArgContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class SubstitutionContext: ArgContext {
			open
			func INTERPOLATION_START() -> TerminalNode? {
				return getToken(ShellParser.Tokens.INTERPOLATION_START.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
			}

		public
		init(_ ctx: ArgContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class ArgErrorContext: ArgContext {
			open
			func LDQUOTE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.LDQUOTE.rawValue, 0)
			}
			open
			func RDQUOTE() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.RDQUOTE.rawValue)
			}
			open
			func RDQUOTE(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.RDQUOTE.rawValue, i)
			}
			open
			func dStringFragment() -> [DStringFragmentContext] {
				return getRuleContexts(DStringFragmentContext.self)
			}
			open
			func dStringFragment(_ i: Int) -> DStringFragmentContext? {
				return getRuleContext(DStringFragmentContext.self, i)
			}
			open
			func INTERPOLATION_START() -> TerminalNode? {
				return getToken(ShellParser.Tokens.INTERPOLATION_START.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.RPAR.rawValue)
			}
			open
			func RPAR(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, i)
			}

		public
		init(_ ctx: ArgContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class SQuotedStringContext: ArgContext {
			open
			func SINGLE_QUOTED_STRING() -> TerminalNode? {
				return getToken(ShellParser.Tokens.SINGLE_QUOTED_STRING.rawValue, 0)
			}

		public
		init(_ ctx: ArgContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class DQuotedStringContext: ArgContext {
			open
			func LDQUOTE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.LDQUOTE.rawValue, 0)
			}
			open
			func RDQUOTE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.RDQUOTE.rawValue, 0)
			}
			open
			func dStringFragment() -> [DStringFragmentContext] {
				return getRuleContexts(DStringFragmentContext.self)
			}
			open
			func dStringFragment(_ i: Int) -> DStringFragmentContext? {
				return getRuleContext(DStringFragmentContext.self, i)
			}

		public
		init(_ ctx: ArgContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	@discardableResult
	 open func arg() throws -> ArgContext {
		var _localctx: ArgContext
		_localctx = ArgContext(_ctx, getState())
		try enterRule(_localctx, 6, ShellParser.RULE_arg)
		var _la: Int = 0
		defer {
	    		try! exitRule()
	    }
		do {
			var _alt:Int
		 	setState(161)
		 	try _errHandler.sync(self)
		 	switch(try getInterpreter().adaptivePredict(_input,20, _ctx)) {
		 	case 1:
		 		_localctx =  WordContext(_localctx);
		 		try enterOuterAlt(_localctx, 1)
		 		setState(118)
		 		try match(ShellParser.Tokens.ARG.rawValue)

		 		break
		 	case 2:
		 		_localctx =  WordContext(_localctx);
		 		try enterOuterAlt(_localctx, 2)
		 		setState(119)
		 		try match(ShellParser.Tokens.WORD.rawValue)

		 		break
		 	case 3:
		 		_localctx =  SQuotedStringContext(_localctx);
		 		try enterOuterAlt(_localctx, 3)
		 		setState(120)
		 		try match(ShellParser.Tokens.SINGLE_QUOTED_STRING.rawValue)

		 		break
		 	case 4:
		 		_localctx =  DQuotedStringContext(_localctx);
		 		try enterOuterAlt(_localctx, 4)
		 		setState(121)
		 		try match(ShellParser.Tokens.LDQUOTE.rawValue)
		 		setState(125)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 		while (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 15032385536) != 0)) {
		 			setState(122)
		 			try dStringFragment()


		 			setState(127)
		 			try _errHandler.sync(self)
		 			_la = try _input.LA(1)
		 		}
		 		setState(128)
		 		try match(ShellParser.Tokens.RDQUOTE.rawValue)

		 		break
		 	case 5:
		 		_localctx =  SubstitutionContext(_localctx);
		 		try enterOuterAlt(_localctx, 5)
		 		setState(129)
		 		try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
		 		setState(130)
		 		try cmds()
		 		setState(131)
		 		try match(ShellParser.Tokens.RPAR.rawValue)

		 		break
		 	case 6:
		 		_localctx =  ArgErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 6)
		 		setState(133)
		 		try match(ShellParser.Tokens.LDQUOTE.rawValue)
		 		setState(137)
		 		try _errHandler.sync(self)
		 		_la = try _input.LA(1)
		 		while (((Int64(_la) & ~0x3f) == 0 && ((Int64(1) << _la) & 15032385536) != 0)) {
		 			setState(134)
		 			try dStringFragment()


		 			setState(139)
		 			try _errHandler.sync(self)
		 			_la = try _input.LA(1)
		 		}
		 		setState(140)
		 		try match(ShellParser.Tokens.RDQUOTE.rawValue)
		 		setState(141)
		 		try match(ShellParser.Tokens.RDQUOTE.rawValue)
		 		notifyErrorListeners("Unbalanced quotes")

		 		break
		 	case 7:
		 		_localctx =  ArgErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 7)
		 		setState(143)
		 		try match(ShellParser.Tokens.LDQUOTE.rawValue)
		 		setState(147)
		 		try _errHandler.sync(self)
		 		_alt = try getInterpreter().adaptivePredict(_input,19,_ctx)
		 		while (_alt != 2 && _alt != ATN.INVALID_ALT_NUMBER) {
		 			if ( _alt==1 ) {
		 				setState(144)
		 				try dStringFragment()

		 		 
		 			}
		 			setState(149)
		 			try _errHandler.sync(self)
		 			_alt = try getInterpreter().adaptivePredict(_input,19,_ctx)
		 		}
		 		notifyErrorListeners("Unbalanced quotes")

		 		break
		 	case 8:
		 		_localctx =  ArgErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 8)
		 		setState(151)
		 		try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
		 		setState(152)
		 		try cmds()
		 		setState(153)
		 		try match(ShellParser.Tokens.RPAR.rawValue)
		 		setState(154)
		 		try match(ShellParser.Tokens.RPAR.rawValue)
		 		notifyErrorListeners("Unbalanced parenthesis")

		 		break
		 	case 9:
		 		_localctx =  ArgErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 9)
		 		setState(157)
		 		try match(ShellParser.Tokens.INTERPOLATION_START.rawValue)
		 		setState(158)
		 		try cmds()
		 		notifyErrorListeners("Unbalanced parenthesis")

		 		break
		 	default: break
		 	}
		}
		catch ANTLRException.recognition(let re) {
			_localctx.exception = re
			_errHandler.reportError(self, re)
			try _errHandler.recover(self, re)
		}

		return _localctx
	}

	public class DStringFragmentContext: ParserRuleContext {
		override open
		func getRuleIndex() -> Int {
			return ShellParser.RULE_dStringFragment
		}
	}
	public class InterpolationContext: DStringFragmentContext {
			open
			func INTERPOLATION_START_IN_DSTRING() -> TerminalNode? {
				return getToken(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, 0)
			}

		public
		init(_ ctx: DStringFragmentContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class DStringFragmentErrorContext: DStringFragmentContext {
			open
			func INTERPOLATION_START_IN_DSTRING() -> TerminalNode? {
				return getToken(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue, 0)
			}
			open
			func cmds() -> CmdsContext? {
				return getRuleContext(CmdsContext.self, 0)
			}
			open
			func RPAR() -> [TerminalNode] {
				return getTokens(ShellParser.Tokens.RPAR.rawValue)
			}
			open
			func RPAR(_ i:Int) -> TerminalNode? {
				return getToken(ShellParser.Tokens.RPAR.rawValue, i)
			}

		public
		init(_ ctx: DStringFragmentContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class EscapeSequenceContext: DStringFragmentContext {
			open
			func ESCAPE_SEQUENCE() -> TerminalNode? {
				return getToken(ShellParser.Tokens.ESCAPE_SEQUENCE.rawValue, 0)
			}

		public
		init(_ ctx: DStringFragmentContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	public class TextContext: DStringFragmentContext {
			open
			func TEXT() -> TerminalNode? {
				return getToken(ShellParser.Tokens.TEXT.rawValue, 0)
			}

		public
		init(_ ctx: DStringFragmentContext) {
			super.init()
			copyFrom(ctx)
		}
	}
	@discardableResult
	 open func dStringFragment() throws -> DStringFragmentContext {
		var _localctx: DStringFragmentContext
		_localctx = DStringFragmentContext(_ctx, getState())
		try enterRule(_localctx, 8, ShellParser.RULE_dStringFragment)
		defer {
	    		try! exitRule()
	    }
		do {
		 	setState(179)
		 	try _errHandler.sync(self)
		 	switch(try getInterpreter().adaptivePredict(_input,21, _ctx)) {
		 	case 1:
		 		_localctx =  TextContext(_localctx);
		 		try enterOuterAlt(_localctx, 1)
		 		setState(163)
		 		try match(ShellParser.Tokens.TEXT.rawValue)

		 		break
		 	case 2:
		 		_localctx =  EscapeSequenceContext(_localctx);
		 		try enterOuterAlt(_localctx, 2)
		 		setState(164)
		 		try match(ShellParser.Tokens.ESCAPE_SEQUENCE.rawValue)

		 		break
		 	case 3:
		 		_localctx =  InterpolationContext(_localctx);
		 		try enterOuterAlt(_localctx, 3)
		 		setState(165)
		 		try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
		 		setState(166)
		 		try cmds()
		 		setState(167)
		 		try match(ShellParser.Tokens.RPAR.rawValue)

		 		break
		 	case 4:
		 		_localctx =  DStringFragmentErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 4)
		 		setState(169)
		 		try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
		 		setState(170)
		 		try cmds()
		 		setState(171)
		 		try match(ShellParser.Tokens.RPAR.rawValue)
		 		setState(172)
		 		try match(ShellParser.Tokens.RPAR.rawValue)
		 		notifyErrorListeners("Unbalanced parenthesis")

		 		break
		 	case 5:
		 		_localctx =  DStringFragmentErrorContext(_localctx);
		 		try enterOuterAlt(_localctx, 5)
		 		setState(175)
		 		try match(ShellParser.Tokens.INTERPOLATION_START_IN_DSTRING.rawValue)
		 		setState(176)
		 		try cmds()
		 		notifyErrorListeners("Unbalanced parenthesis")

		 		break
		 	default: break
		 	}
		}
		catch ANTLRException.recognition(let re) {
			_localctx.exception = re
			_errHandler.reportError(self, re)
			try _errHandler.recover(self, re)
		}

		return _localctx
	}

	override open
	func sempred(_ _localctx: RuleContext?, _ ruleIndex: Int,  _ predIndex: Int)throws -> Bool {
		switch (ruleIndex) {
		case  2:
			return try cmd_sempred(_localctx?.castdown(CmdContext.self), predIndex)
	    default: return true
		}
	}
	private func cmd_sempred(_ _localctx: CmdContext!,  _ predIndex: Int) throws -> Bool {
		switch (predIndex) {
		    case 0:return precpred(_ctx, 6)
		    case 1:return precpred(_ctx, 5)
		    case 2:return precpred(_ctx, 4)
		    default: return true
		}
	}

	static let _serializedATN:[Int] = [
		4,1,34,182,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,1,0,5,0,12,8,0,10,0,
		12,0,15,9,0,1,0,1,0,1,0,1,0,3,0,21,8,0,1,1,1,1,1,1,1,1,3,1,27,8,1,1,1,
		1,1,1,1,1,1,3,1,33,8,1,5,1,35,8,1,10,1,12,1,38,9,1,1,1,1,1,3,1,42,8,1,
		3,1,44,8,1,1,1,1,1,1,1,1,1,4,1,50,8,1,11,1,12,1,51,1,1,5,1,55,8,1,10,1,
		12,1,58,9,1,1,1,3,1,61,8,1,1,2,1,2,1,2,5,2,66,8,2,10,2,12,2,69,9,2,1,2,
		1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,3,2,85,8,2,1,2,1,2,
		5,2,89,8,2,10,2,12,2,92,9,2,1,2,1,2,1,2,1,2,5,2,98,8,2,10,2,12,2,101,9,
		2,1,2,1,2,1,2,1,2,5,2,107,8,2,10,2,12,2,110,9,2,1,2,1,2,5,2,114,8,2,10,
		2,12,2,117,9,2,1,3,1,3,1,3,1,3,1,3,5,3,124,8,3,10,3,12,3,127,9,3,1,3,1,
		3,1,3,1,3,1,3,1,3,1,3,5,3,136,8,3,10,3,12,3,139,9,3,1,3,1,3,1,3,1,3,1,
		3,5,3,146,8,3,10,3,12,3,149,9,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,
		3,1,3,3,3,162,8,3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,
		1,4,1,4,1,4,3,4,180,8,4,1,4,1,56,1,4,5,0,2,4,6,8,0,1,1,0,23,24,212,0,13,
		1,0,0,0,2,60,1,0,0,0,4,84,1,0,0,0,6,161,1,0,0,0,8,179,1,0,0,0,10,12,5,
		24,0,0,11,10,1,0,0,0,12,15,1,0,0,0,13,11,1,0,0,0,13,14,1,0,0,0,14,20,1,
		0,0,0,15,13,1,0,0,0,16,17,3,2,1,0,17,18,5,0,0,1,18,21,1,0,0,0,19,21,5,
		0,0,1,20,16,1,0,0,0,20,19,1,0,0,0,21,1,1,0,0,0,22,23,5,8,0,0,23,24,3,4,
		2,0,24,26,5,12,0,0,25,27,3,2,1,0,26,25,1,0,0,0,26,27,1,0,0,0,27,36,1,0,
		0,0,28,29,5,7,0,0,29,30,3,4,2,0,30,32,5,12,0,0,31,33,3,2,1,0,32,31,1,0,
		0,0,32,33,1,0,0,0,33,35,1,0,0,0,34,28,1,0,0,0,35,38,1,0,0,0,36,34,1,0,
		0,0,36,37,1,0,0,0,37,43,1,0,0,0,38,36,1,0,0,0,39,41,5,13,0,0,40,42,3,2,
		1,0,41,40,1,0,0,0,41,42,1,0,0,0,42,44,1,0,0,0,43,39,1,0,0,0,43,44,1,0,
		0,0,44,45,1,0,0,0,45,46,5,18,0,0,46,61,1,0,0,0,47,49,3,4,2,0,48,50,7,0,
		0,0,49,48,1,0,0,0,50,51,1,0,0,0,51,49,1,0,0,0,51,52,1,0,0,0,52,56,1,0,
		0,0,53,55,3,2,1,0,54,53,1,0,0,0,55,58,1,0,0,0,56,57,1,0,0,0,56,54,1,0,
		0,0,57,61,1,0,0,0,58,56,1,0,0,0,59,61,3,4,2,0,60,22,1,0,0,0,60,47,1,0,
		0,0,60,59,1,0,0,0,61,3,1,0,0,0,62,63,6,2,-1,0,63,67,5,25,0,0,64,66,3,6,
		3,0,65,64,1,0,0,0,66,69,1,0,0,0,67,65,1,0,0,0,67,68,1,0,0,0,68,85,1,0,
		0,0,69,67,1,0,0,0,70,71,5,4,0,0,71,72,3,2,1,0,72,73,5,6,0,0,73,85,1,0,
		0,0,74,75,5,4,0,0,75,76,3,2,1,0,76,77,5,6,0,0,77,78,5,6,0,0,78,79,6,2,
		-1,0,79,85,1,0,0,0,80,81,5,4,0,0,81,82,3,2,1,0,82,83,6,2,-1,0,83,85,1,
		0,0,0,84,62,1,0,0,0,84,70,1,0,0,0,84,74,1,0,0,0,84,80,1,0,0,0,85,115,1,
		0,0,0,86,90,10,6,0,0,87,89,5,24,0,0,88,87,1,0,0,0,89,92,1,0,0,0,90,88,
		1,0,0,0,90,91,1,0,0,0,91,93,1,0,0,0,92,90,1,0,0,0,93,94,5,21,0,0,94,114,
		3,4,2,7,95,99,10,5,0,0,96,98,5,24,0,0,97,96,1,0,0,0,98,101,1,0,0,0,99,
		97,1,0,0,0,99,100,1,0,0,0,100,102,1,0,0,0,101,99,1,0,0,0,102,103,5,20,
		0,0,103,114,3,4,2,6,104,108,10,4,0,0,105,107,5,24,0,0,106,105,1,0,0,0,
		107,110,1,0,0,0,108,106,1,0,0,0,108,109,1,0,0,0,109,111,1,0,0,0,110,108,
		1,0,0,0,111,112,5,22,0,0,112,114,3,4,2,5,113,86,1,0,0,0,113,95,1,0,0,0,
		113,104,1,0,0,0,114,117,1,0,0,0,115,113,1,0,0,0,115,116,1,0,0,0,116,5,
		1,0,0,0,117,115,1,0,0,0,118,162,5,26,0,0,119,162,5,25,0,0,120,162,5,2,
		0,0,121,125,5,3,0,0,122,124,3,8,4,0,123,122,1,0,0,0,124,127,1,0,0,0,125,
		123,1,0,0,0,125,126,1,0,0,0,126,128,1,0,0,0,127,125,1,0,0,0,128,162,5,
		34,0,0,129,130,5,5,0,0,130,131,3,2,1,0,131,132,5,6,0,0,132,162,1,0,0,0,
		133,137,5,3,0,0,134,136,3,8,4,0,135,134,1,0,0,0,136,139,1,0,0,0,137,135,
		1,0,0,0,137,138,1,0,0,0,138,140,1,0,0,0,139,137,1,0,0,0,140,141,5,34,0,
		0,141,142,5,34,0,0,142,162,6,3,-1,0,143,147,5,3,0,0,144,146,3,8,4,0,145,
		144,1,0,0,0,146,149,1,0,0,0,147,145,1,0,0,0,147,148,1,0,0,0,148,150,1,
		0,0,0,149,147,1,0,0,0,150,162,6,3,-1,0,151,152,5,5,0,0,152,153,3,2,1,0,
		153,154,5,6,0,0,154,155,5,6,0,0,155,156,6,3,-1,0,156,162,1,0,0,0,157,158,
		5,5,0,0,158,159,3,2,1,0,159,160,6,3,-1,0,160,162,1,0,0,0,161,118,1,0,0,
		0,161,119,1,0,0,0,161,120,1,0,0,0,161,121,1,0,0,0,161,129,1,0,0,0,161,
		133,1,0,0,0,161,143,1,0,0,0,161,151,1,0,0,0,161,157,1,0,0,0,162,7,1,0,
		0,0,163,180,5,31,0,0,164,180,5,33,0,0,165,166,5,32,0,0,166,167,3,2,1,0,
		167,168,5,6,0,0,168,180,1,0,0,0,169,170,5,32,0,0,170,171,3,2,1,0,171,172,
		5,6,0,0,172,173,5,6,0,0,173,174,6,4,-1,0,174,180,1,0,0,0,175,176,5,32,
		0,0,176,177,3,2,1,0,177,178,6,4,-1,0,178,180,1,0,0,0,179,163,1,0,0,0,179,
		164,1,0,0,0,179,165,1,0,0,0,179,169,1,0,0,0,179,175,1,0,0,0,180,9,1,0,
		0,0,22,13,20,26,32,36,41,43,51,56,60,67,84,90,99,108,113,115,125,137,147,
		161,179
	]

	public
	static let _ATN = try! ATNDeserializer().deserialize(_serializedATN)
}
```

## /Sources/AeroSpaceApp/AeroSpaceApp.swift

```swift path="/Sources/AeroSpaceApp/AeroSpaceApp.swift" 
import AppBundle
import SwiftUI

// This file is shared between SPM and xcode project

@main
struct AeroSpaceApp: App {
    @StateObject var viewModel = TrayMenuModel.shared
    @StateObject var messageModel = MessageModel.shared
    @Environment(\.openWindow) var openWindow: OpenWindowAction

    init() {
        initAppBundle()
    }

    var body: some Scene {
        menuBar(viewModel: viewModel)
        getMessageWindow(messageModel: messageModel)
            .onChange(of: messageModel.message) { message in
                if message != nil {
                    openWindow(id: messageWindowId)
                }
            }
    }
}

```

## /Sources/AppBundle/GlobalObserver.swift

```swift path="/Sources/AppBundle/GlobalObserver.swift" 
import AppKit
import Common

enum GlobalObserver {
    private static func onNotif(_ notification: Notification) {
        // Third line of defence against lock screen window. See: closedWindowsCache
        // Second and third lines of defence are technically needed only to avoid potential flickering
        if (notification.userInfo?[NSWorkspace.applicationUserInfoKey] as? NSRunningApplication)?.bundleIdentifier == lockScreenAppBundleId {
            return
        }
        let notifName = notification.name.rawValue
        Task { @MainActor in
            if !TrayMenuModel.shared.isEnabled { return }
            if notifName == NSWorkspace.didActivateApplicationNotification.rawValue {
                runRefreshSession(.globalObserver(notifName), optimisticallyPreLayoutWorkspaces: true)
            } else {
                runRefreshSession(.globalObserver(notifName))
            }
        }
    }

    private static func onHideApp(_ notification: Notification) {
        let notifName = notification.name.rawValue
        Task { @MainActor in
            guard let token: RunSessionGuard = .isServerEnabled else { return }
            try await runSession(.globalObserver(notifName), token) {
                if config.automaticallyUnhideMacosHiddenApps {
                    if let w = prevFocus?.windowOrNil,
                       w.macAppUnsafe.nsApp.isHidden,
                       // "Hide others" (cmd-alt-h) -> don't force focus
                       // "Hide app" (cmd-h) -> force focus
                       MacApp.allAppsMap.values.count(where: { $0.nsApp.isHidden }) == 1
                    {
                        // Force focus
                        _ = w.focusWindow()
                        w.nativeFocus()
                    }
                    for app in MacApp.allAppsMap.values {
                        app.nsApp.unhide()
                    }
                }
            }
        }
    }

    @MainActor
    static func initObserver() {
        let nc = NSWorkspace.shared.notificationCenter
        nc.addObserver(forName: NSWorkspace.didLaunchApplicationNotification, object: nil, queue: .main, using: onNotif)
        nc.addObserver(forName: NSWorkspace.didActivateApplicationNotification, object: nil, queue: .main, using: onNotif)
        nc.addObserver(forName: NSWorkspace.didHideApplicationNotification, object: nil, queue: .main, using: onHideApp)
        nc.addObserver(forName: NSWorkspace.didUnhideApplicationNotification, object: nil, queue: .main, using: onNotif)
        nc.addObserver(forName: NSWorkspace.activeSpaceDidChangeNotification, object: nil, queue: .main, using: onNotif)
        nc.addObserver(forName: NSWorkspace.didTerminateApplicationNotification, object: nil, queue: .main, using: onNotif)

        NSEvent.addGlobalMonitorForEvents(matching: .leftMouseUp) { _ in
            // todo reduce number of refreshSession in the callback
            //  resetManipulatedWithMouseIfPossible might call its own refreshSession
            //  The end of the callback calls refreshSession
            Task { @MainActor in
                guard let token: RunSessionGuard = .isServerEnabled else { return }
                try await resetManipulatedWithMouseIfPossible()
                let mouseLocation = mouseLocation
                let clickedMonitor = mouseLocation.monitorApproximation
                switch true {
                    // Detect clicks on desktop of different monitors
                    case clickedMonitor.activeWorkspace != focus.workspace:
                        _ = try await runSession(.globalObserverLeftMouseUp, token) {
                            clickedMonitor.activeWorkspace.focusWorkspace()
                        }
                    // Detect close button clicks for unfocused windows. Yes, kAXUIElementDestroyedNotification is that unreliable
                    //  And trigger new window detection that could be delayed due to mouseDown event
                    default:
                        runRefreshSession(.globalObserverLeftMouseUp)
                }
            }
        }
    }
}

```

## /Sources/AppBundle/command/CmdEnv.swift

```swift path="/Sources/AppBundle/command/CmdEnv.swift" 
import Common

struct CmdEnv: ConvenienceCopyable { // todo forward env from cli to server
    var windowId: UInt32?
    var workspaceName: String?
    var pwd: String?

    static var defaultEnv: CmdEnv { CmdEnv(windowId: nil, workspaceName: nil, pwd: nil) }
    func withFocus(_ focus: LiveFocus) -> CmdEnv {
        switch focus.asLeaf {
            case .window(let wd): .defaultEnv.copy(\.windowId, wd.windowId)
            case .emptyWorkspace(let ws): .defaultEnv.copy(\.workspaceName, ws.name)
        }
    }

    @MainActor
    var asMap: [String: String] {
        var result = config.execConfig.envVariables
        if let pwd {
            result["PWD"] = pwd
        }
        if let windowId {
            result[AEROSPACE_WINDOW_ID] = windowId.description
        }
        if let workspaceName {
            result[AEROSPACE_WORKSPACE] = workspaceName.description
        }
        return result
    }
}

```

## /Sources/AppBundle/command/CmdIo.swift

```swift path="/Sources/AppBundle/command/CmdIo.swift" 
final class CmdStdin {
    private var input: String = ""
    init(_ input: String) {
        self.input = input
    }
    static var emptyStdin: CmdStdin { .init("") }

    func readAll() -> String {
        let result = input
        input = ""
        return result
    }
}

final class CmdIo {
    private var stdin: CmdStdin
    var stdout: [String] = []
    var stderr: [String] = []

    init(stdin: CmdStdin) { self.stdin = stdin }

    @discardableResult func out(_ msg: String) -> Bool { stdout.append(msg); return true }
    @discardableResult func err(_ msg: String) -> Bool { stderr.append(msg); return false }
    @discardableResult func out(_ msg: [String]) -> Bool { stdout += msg; return true }
    @discardableResult func err(_ msg: [String]) -> Bool { stderr += msg; return false }

    func readStdin() -> String { stdin.readAll() }
}

struct CmdResult {
    let stdout: [String]
    let stderr: [String]
    let exitCode: Int32
}

```

## /Sources/AppBundle/command/Command.swift

```swift path="/Sources/AppBundle/command/Command.swift" 
import AppKit
import Common

protocol Command: AeroAny, Equatable, Sendable {
    associatedtype T where T: CmdArgs
    var args: T { get }
    @MainActor
    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool

    /// We should reset closedWindowsCache when the command can potentiall change the tree
    var shouldResetClosedWindowsCache: Bool { get }
}

extension Command {
    static func == (lhs: Self, rhs: Self) -> Bool {
        return lhs.args.equals(rhs.args)
    }

    nonisolated func equals(_ other: any Command) -> Bool {
        (other as? Self).flatMap { self == $0 } ?? false
    }
}

extension Command {
    var info: CmdStaticInfo { T.info }
}

extension Command {
    @MainActor
    @discardableResult
    func run(_ env: CmdEnv, _ stdin: CmdStdin) async throws -> CmdResult {
        return try await [self].runCmdSeq(env, stdin)
    }

    var isExec: Bool { self is ExecAndForgetCommand }
}

// There are 4 entry points for running commands:
// 1. config keybindings
// 2. CLI requests to server
// 3. on-window-detected callback
// 4. Tray icon buttons
extension [Command] {
    @MainActor
    func runCmdSeq(_ env: CmdEnv, _ io: sending CmdIo) async throws -> Bool {
        var isSucc = true
        for command in self {
            isSucc = try await command.run(env, io) && isSucc
            if command.shouldResetClosedWindowsCache { resetClosedWindowsCache() }
            refreshModel()
        }
        return isSucc
    }

    @MainActor
    func runCmdSeq(_ env: CmdEnv, _ stdin: CmdStdin) async throws -> CmdResult {
        let io: CmdIo = CmdIo(stdin: stdin)
        let isSucc = try await runCmdSeq(env, io)
        return CmdResult(stdout: io.stdout, stderr: io.stderr, exitCode: isSucc ? 0 : 1)
    }
}

```

## /Sources/AppBundle/command/cmdManifest.swift

```swift path="/Sources/AppBundle/command/cmdManifest.swift" 
import Common

extension CmdArgs {
    func toCommand() -> any Command {
        let command: any Command
        switch Self.info.kind {
            case .balanceSizes:
                command = BalanceSizesCommand(args: self as! BalanceSizesCmdArgs)
            case .close:
                command = CloseCommand(args: self as! CloseCmdArgs)
            case .closeAllWindowsButCurrent:
                command = CloseAllWindowsButCurrentCommand(args: self as! CloseAllWindowsButCurrentCmdArgs)
            case .config:
                command = ConfigCommand(args: self as! ConfigCmdArgs)
            case .debugWindows:
                command = DebugWindowsCommand(args: self as! DebugWindowsCmdArgs)
            case .enable:
                command = EnableCommand(args: self as! EnableCmdArgs)
            case .execAndForget:
                die("exec-and-forget is parsed separately")
            case .flattenWorkspaceTree:
                command = FlattenWorkspaceTreeCommand(args: self as! FlattenWorkspaceTreeCmdArgs)
            case .focus:
                command = FocusCommand(args: self as! FocusCmdArgs)
            case .focusBackAndForth:
                command = FocusBackAndForthCommand(args: self as! FocusBackAndForthCmdArgs)
            case .focusMonitor:
                command = FocusMonitorCommand(args: self as! FocusMonitorCmdArgs)
            case .fullscreen:
                command = FullscreenCommand(args: self as! FullscreenCmdArgs)
            case .joinWith:
                command = JoinWithCommand(args: self as! JoinWithCmdArgs)
            case .layout:
                command = LayoutCommand(args: self as! LayoutCmdArgs)
            case .listApps:
                command = ListAppsCommand(args: self as! ListAppsCmdArgs)
            case .listExecEnvVars:
                command = ListExecEnvVarsCommand(args: self as! ListExecEnvVarsCmdArgs)
            case .listModes:
                command = ListModesCommand(args: self as! ListModesCmdArgs)
            case .listMonitors:
                command = ListMonitorsCommand(args: self as! ListMonitorsCmdArgs)
            case .listWindows:
                command = ListWindowsCommand(args: self as! ListWindowsCmdArgs)
            case .listWorkspaces:
                command = ListWorkspacesCommand(args: self as! ListWorkspacesCmdArgs)
            case .macosNativeFullscreen:
                command = MacosNativeFullscreenCommand(args: self as! MacosNativeFullscreenCmdArgs)
            case .macosNativeMinimize:
                command = MacosNativeMinimizeCommand(args: self as! MacosNativeMinimizeCmdArgs)
            case .mode:
                command = ModeCommand(args: self as! ModeCmdArgs)
            case .move:
                command = MoveCommand(args: self as! MoveCmdArgs)
            case .moveMouse:
                command = MoveMouseCommand(args: self as! MoveMouseCmdArgs)
            case .moveNodeToMonitor:
                command = MoveNodeToMonitorCommand(args: self as! MoveNodeToMonitorCmdArgs)
            case .moveNodeToWorkspace:
                command = MoveNodeToWorkspaceCommand(args: self as! MoveNodeToWorkspaceCmdArgs)
            case .moveWorkspaceToMonitor:
                command = MoveWorkspaceToMonitorCommand(args: self as! MoveWorkspaceToMonitorCmdArgs)
            case .reloadConfig:
                command = ReloadConfigCommand(args: self as! ReloadConfigCmdArgs)
            case .resize:
                command = ResizeCommand(args: self as! ResizeCmdArgs)
            case .split:
                command = SplitCommand(args: self as! SplitCmdArgs)
            case .summonWorkspace:
                command = SummonWorkspaceCommand(args: self as! SummonWorkspaceCmdArgs)
            case .swap:
                command = SwapCommand(args: self as! SwapCmdArgs)
            case .triggerBinding:
                command = TriggerBindingCommand(args: self as! TriggerBindingCmdArgs)
            case .volume:
                command = VolumeCommand(args: self as! VolumeCmdArgs)
            case .workspace:
                command = WorkspaceCommand(args: self as! WorkspaceCmdArgs)
            case .workspaceBackAndForth:
                command = WorkspaceBackAndForthCommand(args: self as! WorkspaceBackAndForthCmdArgs)
        }
        check(command.info == Self.info)
        return command
    }
}

```

## /Sources/AppBundle/command/cmdResolveTargetOrReportError.swift

```swift path="/Sources/AppBundle/command/cmdResolveTargetOrReportError.swift" 
import Common

extension CmdArgs {
    @MainActor
    var workspace: Workspace? {
        if let workspaceName { Workspace.get(byName: workspaceName.raw) } else { nil }
    }

    @MainActor
    func resolveTargetOrReportError(_ env: CmdEnv, _ io: CmdIo) -> LiveFocus? {
        // Flags
        if let windowId {
            if let wi = Window.get(byId: windowId) {
                return wi.toLiveFocusOrReportError(io)
            } else {
                io.err("Invalid <window-id> \(windowId) passed to --window-id")
                return nil
            }
        }
        if let workspace {
            return workspace.toLiveFocus()
        }
        // Env
        if let windowId = env.windowId {
            if let wi = Window.get(byId: windowId) {
                return wi.toLiveFocusOrReportError(io)
            } else {
                io.err("Invalid <window-id> \(windowId) specified in \(AEROSPACE_WINDOW_ID) env variable")
                return nil
            }
        }
        if let wsName = env.workspaceName {
            return Workspace.get(byName: wsName).toLiveFocus()
        }
        // Real Focus
        return focus
    }
}

extension Window {
    @MainActor
    func toLiveFocusOrReportError(_ io: CmdIo) -> LiveFocus? {
        if let result = toLiveFocusOrNil() {
            return result
        } else {
            io.err("Window \(windowId) doesn't belong to any monitor. And thus can't even define a focused workspace")
            return nil
        }
    }
}

```

## /Sources/AppBundle/command/format.swift

```swift path="/Sources/AppBundle/command/format.swift" 
import Common

enum AeroObj {
    case window(window: Window, title: String)
    case workspace(Workspace)
    case app(any AbstractApp)
    case monitor(Monitor)

    var kind: AeroObjKind {
        switch self {
            case .window: .window
            case .workspace: .workspace
            case .app: .app
            case .monitor: .monitor
        }
    }
}

extension [AeroObj] {
    @MainActor
    func format(_ format: [StringInterToken]) -> Result<[String], String> {
        var cellTable: [[Cell<String>]] = []
        for obj in self {
            var line: [Cell<String>] = []
            var curCell: String = ""
            var errors: [String] = []
            for token in format {
                switch token {
                    case .interVar(PlainInterVar.rightPadding.rawValue):
                        line.append(Cell(value: curCell, rightPadding: true))
                        curCell = ""
                    case .literal(let literal):
                        curCell += literal
                    case .interVar(let value):
                        switch value.expandFormatVar(obj: obj) {
                            case .success(let expanded): curCell += expanded.toString()
                            case .failure(let error): errors.append(error)
                        }
                }
            }
            if !errors.isEmpty { return .failure(errors.joinErrors()) }
            line.append(Cell(value: curCell, rightPadding: false))
            cellTable.append(line)
        }
        let result = cellTable
            .transposed()
            .map { column in
                let columndWidth = column.map { $0.value.count }.max().orDie()
                return column.map {
                    $0.rightPadding
                        ? $0.value + String(repeating: " ", count: columndWidth - $0.value.count)
                        : $0.value
                }
            }
            .transposed()
            .map { line in line.joined(separator: "") }
        return .success(result)
    }
}

enum Primitive: Encodable {
    case bool(Bool)
    case int(Int)
    case int32(Int32)
    case uint32(UInt32)
    case string(String)

    func toString() -> String {
        switch self {
            case .bool(let x): x.description
            case .int(let x): x.description
            case .int32(let x): x.description
            case .uint32(let x): x.description
            case .string(let x): x
        }
    }

    func encode(to encoder: any Encoder) throws {
        let value: Encodable = switch self {
            case .bool(let x): x
            case .int(let x): x
            case .int32(let x): x
            case .uint32(let x): x
            case .string(let x): x
        }
        var container = encoder.singleValueContainer()
        try container.encode(value)
    }
}

private struct Cell<T> {
    let value: T
    let rightPadding: Bool
}

extension String {
    @MainActor
    func expandFormatVar(obj: AeroObj) -> Result<Primitive, String> {
        let formatVar = self.toFormatVar()
        switch (obj, formatVar) {
            case (_, .none): break

            case (.window(let w, _), .workspace):
                return w.nodeWorkspace.flatMap(AeroObj.workspace).map(expandFormatVar) ?? .success(.string("NULL-WOKRSPACE"))
            case (.window(let w, _), .monitor):
                return w.nodeMonitor.flatMap(AeroObj.monitor).map(expandFormatVar) ?? .success(.string("NULL-MONITOR"))
            case (.window(let w, _), .app):
                return expandFormatVar(obj: .app(w.app))
            case (.window(_, _), .window): break

            case (.workspace(let ws), .monitor):
                return expandFormatVar(obj: AeroObj.monitor(ws.workspaceMonitor))
            case (.workspace, _): break

            case (.app(_), _): break
            case (.monitor(_), _): break
        }
        switch (obj, formatVar) {
            case (.window(let w, let title), .window(let f)):
                return switch f {
                    case .windowId: .success(.uint32(w.windowId))
                    case .windowIsFullscreen: .success(.bool(w.isFullscreen))
                    case .windowTitle: .success(.string(title))
                    case .windowLayout, .windowParentContainerLayout: toLayoutResult(w: w)
                }
            case (.workspace(let w), .workspace(let f)):
                return switch f {
                    case .workspaceName: .success(.string(w.name))
                    case .workspaceVisible: .success(.bool(w.isVisible))
                    case .workspaceFocused: .success(.bool(focus.workspace == w))
                    case .workspaceRootContainerLayout: .success(.string(toLayoutString(tc: w.rootTilingContainer)))
                }
            case (.monitor(let m), .monitor(let f)):
                return switch f {
                    case .monitorId: .success(m.monitorId.map { .int($0 + 1) } ?? .string("NULL-MONITOR-ID"))
                    case .monitorAppKitNsScreenScreensId: .success(.int(m.monitorAppKitNsScreenScreensId))
                    case .monitorName: .success(.string(m.name))
                    case .monitorIsMain: .success(.bool(m.isMain))
                }
            case (.app(let a), .app(let f)):
                return switch f {
                    case .appBundleId: .success(.string(a.rawAppBundleId ?? "NULL-APP-BUNDLE-ID"))
                    case .appName: .success(.string(a.name ?? "NULL-APP-NAME"))
                    case .appPid: .success(.int32(a.pid))
                    case .appExecPath: .success(.string(a.execPath ?? "NULL-APP-EXEC-PATH"))
                    case .appBundlePath: .success(.string(a.bundlePath ?? "NULL-APP-BUNDLE-PATH"))
                }
            default: break
        }
        if self == PlainInterVar.newline.rawValue { return .success(.string("\n")) }
        if self == PlainInterVar.tab.rawValue { return .success(.string("\t")) }
        return .failure("Unknown interpolation variable '\(self)'. " +
            "Possible values:\n\(getAvailableInterVars(for: obj.kind).joined(separator: "\n").prependLines("  "))")
    }

    private func toFormatVar() -> FormatVar? {
        FormatVar.WindowFormatVar(rawValue: self).flatMap(FormatVar.window)
            ?? FormatVar.WorkspaceFormatVar(rawValue: self).flatMap(FormatVar.workspace)
            ?? FormatVar.AppFormatVar(rawValue: self).flatMap(FormatVar.app)
            ?? FormatVar.MonitorFormatVar(rawValue: self).flatMap(FormatVar.monitor)
    }
}

private func toLayoutString(tc: TilingContainer) -> String {
    switch (tc.layout, tc.orientation) {
        case (.tiles, .h): return LayoutCmdArgs.LayoutDescription.h_tiles.rawValue
        case (.tiles, .v): return LayoutCmdArgs.LayoutDescription.v_tiles.rawValue
        case (.accordion, .h): return LayoutCmdArgs.LayoutDescription.h_accordion.rawValue
        case (.accordion, .v): return LayoutCmdArgs.LayoutDescription.v_accordion.rawValue
    }
}

private func toLayoutResult(w: Window) -> Result<Primitive, String> {
    guard let parent = w.parent else { return .failure("NULL-PARENT") }
    return switch getChildParentRelation(child: w, parent: parent) {
        case .tiling(let tc): .success(.string(toLayoutString(tc: tc)))
        case .floatingWindow: .success(.string(LayoutCmdArgs.LayoutDescription.floating.rawValue))
        case .macosNativeFullscreenWindow: .success(.string("macos_native_fullscreen"))
        case .macosNativeHiddenAppWindow: .success(.string("macos_native_window_of_hidden_app"))
        case .macosNativeMinimizedWindow: .success(.string("macos_native_minimized"))
        case .macosPopupWindow: .success(.string("NULL-WINDOW-LAYOUT"))

        case .rootTilingContainer: .failure("Not possible")
        case .shimContainerRelation: .failure("Window cannot have a shim container relation")
    }
}

```

## /Sources/AppBundle/command/formatToJson.swift

```swift path="/Sources/AppBundle/command/formatToJson.swift" 
import Common
import Foundation

extension [AeroObj] {
    @MainActor
    func formatToJson(_ format: [StringInterToken], ignoreRightPaddingVar: Bool) -> Result<String, String> {
        var list: [[String: Primitive]] = []
        for richObj in self {
            var rawObj: [String: Primitive] = [:]
            for token in format {
                switch token {
                    case .interVar(PlainInterVar.rightPadding.rawValue) where ignoreRightPaddingVar:
                        break
                    case .literal:
                        break // should be spaces
                    case .interVar(let varName):
                        switch varName.expandFormatVar(obj: richObj) {
                            case .success(let expanded): rawObj[varName] = expanded
                            case .failure(let error): return .failure(error)
                        }
                }
            }
            list.append(rawObj)
        }
        return JSONEncoder.aeroSpaceDefault.encodeToString(list).map(Result.success)
            ?? .failure("Can't encode '\(list)' to JSON")
    }
}

```

## /Sources/AppBundle/command/impl/BalanceSizesCommand.swift

```swift path="/Sources/AppBundle/command/impl/BalanceSizesCommand.swift" 
import AppKit
import Common
import Foundation

struct BalanceSizesCommand: Command {
    let args: BalanceSizesCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        balance(target.workspace.rootTilingContainer)
        return true
    }
}

@MainActor
private func balance(_ parent: TilingContainer) {
    for child in parent.children {
        switch parent.layout {
            case .tiles: child.setWeight(parent.orientation, 1)
            case .accordion: break // Do nothing
        }
        if let child = child as? TilingContainer {
            balance(child)
        }
    }
}

```

## /Sources/AppBundle/command/impl/CloseAllWindowsButCurrentCommand.swift

```swift path="/Sources/AppBundle/command/impl/CloseAllWindowsButCurrentCommand.swift" 
import AppKit
import Common

struct CloseAllWindowsButCurrentCommand: Command {
    let args: CloseAllWindowsButCurrentCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let focused = target.windowOrNil else {
            return io.err("Empty workspace")
        }
        guard let workspace = focused.nodeWorkspace else {
            return io.err("Focused window '\(focused.windowId)' doesn't belong to workspace")
        }
        var result = true
        for window in workspace.allLeafWindowsRecursive where window != focused {
            result = try await CloseCommand(args: args.closeArgs).run(env.copy(\.windowId, window.windowId), io) && result
        }
        return result
    }
}

```

## /Sources/AppBundle/command/impl/CloseCommand.swift

```swift path="/Sources/AppBundle/command/impl/CloseCommand.swift" 
import AppKit
import Common

struct CloseCommand: Command {
    let args: CloseCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        try await allowOnlyCancellationError {
            guard let target = args.resolveTargetOrReportError(env, io) else { return false }
            guard let window = target.windowOrNil else {
                return io.err("Empty workspace")
            }
            // Access ax directly. Not cool :(
            if try await args.quitIfLastWindow.andAsync({ @MainActor @Sendable in try await window.macAppUnsafe.getAxWindowsCount() == 1 }) {
                let app = window.macAppUnsafe
                if app.nsApp.terminate() {
                    for workspace in Workspace.all {
                        for window in workspace.allLeafWindowsRecursive where window.app.pid == app.pid {
                            (window as! MacWindow).garbageCollect(skipClosedWindowsCache: true)
                        }
                    }
                    return true
                } else {
                    return io.err("Failed to quit '\(window.app.name ?? "Unknown app")'")
                }
            } else {
                window.closeAxWindow()
                return true
            }
        }
    }
}

```

## /Sources/AppBundle/command/impl/ConfigCommand.swift

```swift path="/Sources/AppBundle/command/impl/ConfigCommand.swift" 
import AppKit
import Common

struct ConfigCommand: Command {
    let args: ConfigCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        switch args.mode {
            case .getKey(let key):
                return getKey(io, args: args, key: key)
            case .majorKeys:
                let out = """
                    .
                    mode
                    \(config.modes.keys.map { "mode.\($0).binding" }.joined(separator: "\n"))
                    """
                return io.out(out)
            case .allKeys:
                let configMap = buildConfigMap()
                var allKeys: [String] = []
                configMap.dumpAllKeysRecursive(path: ".", result: &allKeys)
                return io.out(allKeys.joined(separator: "\n"))
            case .configPath:
                return io.out(configUrl.absoluteURL.path)
        }
    }
}

extension String {
    fileprivate func toKeyPath() -> Result<[String], String> {
        if self == "." { return .success([]) }
        if isEmpty { return .failure("Invalid empty key") }
        if self.contains("..") { return .failure("Invalid key '\(self)'") }
        if self.hasSuffix(".") { return .failure("Invalid key '\(self)'") }
        return .success(self.split(separator: ".", omittingEmptySubsequences: false).map(String.init))
    }
}

@MainActor private func getKey(_ io: CmdIo, args: ConfigCmdArgs, key: String) -> Bool {
    let keyPath: [String]
    switch key.toKeyPath() {
        case .success(let _keyPath): keyPath = _keyPath
        case .failure(let error):
            return io.err(error)
    }
    var configMap: ConfigMapValue
    switch buildConfigMap().find(keyPath: keyPath.slice) {
        case .success(let value):
            configMap = value
        case .failure(let error):
            return io.err(error)
    }
    if args.keys {
        switch configMap {
            case .scalar(let scalar):
                return io.err("--keys flag cannot be applied to scalar object '\(scalar)'")
            case .map(let map):
                configMap = .array(map.keys.map { .scalar(.string($0)) })
            case .array(let array):
                configMap = .array((0 ..< array.count).map { .scalar(.int($0)) })
        }
    }
    if args.json {
        if let json = JSONEncoder.aeroSpaceDefault.encodeToString(configMap) {
            return io.out(json)
        } else {
            return io.err("Can't convert json Data to String")
        }
    } else {
        switch configMap {
            case .scalar(let scalar):
                return io.out(scalar.describe)
            case .map:
                return io.err("Complicated objects can be printed only with --json flag. " +
                    "Alternatively, you can try to inspect keys of the object with --keys flag")
            case .array(let array):
                let plainArray: Result<[String], String> = array.mapAllOrFailure {
                    switch $0 {
                        case .scalar(let scalar): .success(scalar.describe)
                        default: .failure("Printing array of non-string objects is supported only with --json flag." +
                                "Alternatively, you can try to inspect keys of the object with --keys flag")
                    }
                }
                return switch plainArray {
                    case .success(let array): io.out(array.sorted().joined(separator: "\n"))
                    case .failure(let error): io.err(error)
                }
        }
    }
}

extension ConfigMapValue {
    func find(keyPath: StrArrSlice) -> Result<ConfigMapValue, String> {
        if let key = keyPath.first {
            switch self {
                case .scalar(let scalar):
                    return .failure("Can't dereference scalar value '\(scalar.describe)'")
                case .map(let map):
                    if let child = map[key] {
                        return child.find(keyPath: keyPath.slice(1...).orDie())
                    } else {
                        return .failure("No value at key token '\(key)'")
                    }
                case .array(let array):
                    if let key = Int(key) {
                        if let child = array.getOrNil(atIndex: key) {
                            return child.find(keyPath: keyPath.slice(1...).orDie())
                        } else {
                            return .failure("Index out of bounds. Index: \(key), Size: \(array.count)")
                        }
                    } else {
                        return .failure("Can't convert key token '\(key)' to Int")
                    }
            }
        } else {
            return .success(self)
        }
    }

    func dumpAllKeysRecursive(path: String, result: inout [String]) {
        result.append(path)
        switch self {
            case .scalar: break
            case .map(let map):
                for (key, value) in map {
                    let path = path == "." ? key : path + "." + key
                    value.dumpAllKeysRecursive(path: path, result: &result)
                }
            case .array(let array):
                for (index, value) in array.enumerated() {
                    let path = path == "." ? String(index) : path + "." + String(index)
                    value.dumpAllKeysRecursive(path: path, result: &result)
                }
        }
    }
}

extension [Command] {
    var prettyDescription: String {
        map { $0.args.description }.joined(separator: "; ")
    }
}

@MainActor func buildConfigMap() -> ConfigMapValue {
    let mode = config.modes.mapValues { (mode: Mode) -> ConfigMapValue in
        var keyNotationToScript: [String: ConfigMapValue] = [:]
        for binding in mode.bindings.values {
            keyNotationToScript[binding.descriptionWithKeyNotation] =
                .scalar(.string(binding.commands.prettyDescription))
        }
        return .map(["binding": .map(keyNotationToScript)])
    }
    return .map(["mode": .map(mode)])
}

enum ConfigScalarValue: Encodable {
    case string(String)
    case int(Int)

    var describe: String {
        return switch self {
            case .string(let string): string
            case .int(let int): String(int)
        }
    }

    func encode(to encoder: Encoder) throws {
        let value: Encodable = switch self {
            case .string(let string): string
            case .int(let int): int
        }
        var container = encoder.singleValueContainer()
        try container.encode(value)
    }
}

enum ConfigMapValue: Encodable {
    case scalar(ConfigScalarValue)
    case map([String: ConfigMapValue])
    case array([ConfigMapValue])

    func encode(to encoder: Encoder) throws {
        let value: Encodable = switch self {
            case .scalar(let scalar): scalar
            case .map(let map): map
            case .array(let array): array
        }
        var container = encoder.singleValueContainer()
        try container.encode(value)
    }
}

```

## /Sources/AppBundle/command/impl/DebugWindowsCommand.swift

```swift path="/Sources/AppBundle/command/impl/DebugWindowsCommand.swift" 
import AppKit
import Common
import OrderedCollections

private let disclaimer =
    """
    !!! DISCLAIMER !!!
    !!! 'debug-windows' command is not stable API. Please don't rely on the command existence and output format !!!
    !!! The only intended use case is to report bugs about incorrect windows handling !!!
    """

@MainActor private var debugWindowsState: DebugWindowsState = .notRecording
@MainActor private var debugWindowsLog: OrderedDictionary<UInt32, String> = [:]
private let debugWindowsLimit = 10

enum DebugWindowsState {
    case recording
    case notRecording
    case recordingAborted
}

struct DebugWindowsCommand: Command {
    let args: DebugWindowsCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        if let windowId = args.windowId {
            guard let window = Window.get(byId: windowId) else {
                return io.err("Can't find window with the specified window-id: \(windowId)")
            }
            io.out(try await dumpWindowDebugInfo(window) + "\n")
            io.out(disclaimer)
            return true
        }
        switch debugWindowsState {
            case .recording:
                debugWindowsState = .notRecording
                io.out(debugWindowsLog.values.joined(separator: "\n\n"))
                io.out("\n" + disclaimer + "\n")
                io.out("Debug session finished" + "\n")
                debugWindowsLog = [:]
                return true
            case .notRecording:
                debugWindowsState = .recording
                debugWindowsLog = [:]
                io.out(
                    """
                    Debug windows session has started
                    1. Focus the problematic window
                    2. Run 'aerospace debug-windows' once again to finish the session and get the results
                    """,
                )
                // Make sure that the Terminal window that started the recording is recorded first
                guard let target = args.resolveTargetOrReportError(env, io) else { return false }
                if let window = target.windowOrNil {
                    try await debugWindowsIfRecording(window)
                }
                return true
            case .recordingAborted:
                io.out(
                    """
                    Recording of the previous session was aborted after \(debugWindowsLimit) windows has been focused
                    Run the command one more time to start new debug session
                    """,
                )
                debugWindowsState = .notRecording
                debugWindowsLog = [:]
                return false
        }
    }
}

@MainActor
private func dumpWindowDebugInfo(_ window: Window) async throws -> String {
    let window = window as! MacWindow
    let appInfoDic = window.macApp.nsApp.bundleURL.flatMap { Bundle.init(url: $0) }?.infoDictionary ?? [:]

    var result: [String: Json] = try await window.dumpAxInfo()

    result["Aero.axWindowId"] = .uint32(window.windowId)
    result["Aero.workspace"] = .stringOrNull(window.nodeWorkspace?.name)
    result["Aero.treeNodeParent"] = .string(String(describing: window.parent))
    result["Aero.macOS.version"] = .string(ProcessInfo().operatingSystemVersionString) // because built-in apps might behave differently depending on the OS version
    result["Aero.App.appBundleId"] = .stringOrNull(window.app.rawAppBundleId)
    result["Aero.App.pid"] = .int(Int(window.app.pid))
    result["Aero.App.versionShort"] = .stringOrNull(appInfoDic["CFBundleShortVersionString"] as? String)
    result["Aero.App.version"] = .stringOrNull(appInfoDic["CFBundleVersion"] as? String)
    result["Aero.App.nsApp.activationPolicy"] = .string(window.macApp.nsApp.activationPolicy.prettyDescription)
    result["Aero.App.nsApp.execPath"] = .stringOrNull(window.macApp.nsApp.executableURL?.description)
    result["Aero.App.nsApp.appBundlePath"] = .stringOrNull(window.macApp.nsApp.bundleURL?.description)
    result["Aero.AXApp"] = .dict(try await window.macApp.dumpAppAxInfo())

    let isDialog = try await window.isDialogHeuristic()
    let isWindow = try await window.isWindowHeuristic()
    result["Aero.AxUiElementWindowType"] = .string(AxUiElementWindowType.new(isWindow: isWindow, isDialog: { isDialog }).rawValue)
    result["Aero.AxUiElementWindowType_isDialogHeuristic"] = .bool(isDialog)

    var matchingCallbacks: [Json] = []
    for callback in config.onWindowDetected where try await callback.matches(window) {
        matchingCallbacks.append(callback.debugJson)
    }
    result["Aero.on-window-detected"] = .array(matchingCallbacks)

    return JSONEncoder.aeroSpaceDefault.encodeToString(result).prettyDescription
        .prefixLines(with: "\(window.app.rawAppBundleId ?? "nil-bundle-id").\(window.windowId) ||| ")
}

@MainActor
func debugWindowsIfRecording(_ window: Window) async throws {
    switch debugWindowsState {
        case .recording: break
        case .notRecording, .recordingAborted: return
    }
    if debugWindowsLog.count > debugWindowsLimit {
        debugWindowsState = .recordingAborted
        debugWindowsLog = [:]
    }
    if debugWindowsLog.keys.contains(window.windowId) {
        return
    }
    debugWindowsLog[window.windowId] = try await dumpWindowDebugInfo(window)
}

```

## /Sources/AppBundle/command/impl/EnableCommand.swift

```swift path="/Sources/AppBundle/command/impl/EnableCommand.swift" 
import AppKit
import Common

struct EnableCommand: Command {
    let args: EnableCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        let prevState = TrayMenuModel.shared.isEnabled
        let newState: Bool = switch args.targetState.val {
            case .on: true
            case .off: false
            case .toggle: !TrayMenuModel.shared.isEnabled
        }
        if newState == prevState {
            io.out((newState ? "Already enabled" : "Already disabled") +
                "Tip: use --fail-if-noop to exit with non-zero code")
            return !args.failIfNoop
        }

        TrayMenuModel.shared.isEnabled = newState
        if newState {
            for workspace in Workspace.all {
                for window in workspace.allLeafWindowsRecursive where window.isFloating {
                    window.lastFloatingSize = try await window.getAxSize() ?? window.lastFloatingSize
                }
            }
            activateMode(mainModeId)
        } else {
            activateMode(nil)
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/ExecAndForgetCommand.swift

```swift path="/Sources/AppBundle/command/impl/ExecAndForgetCommand.swift" 
import AppKit
import Common

struct ExecAndForgetCommand: Command {
    let args: ExecAndForgetCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        // todo shall exec-and-forget fork exec session?
        // It doesn't throw if exit code is non-zero
        let process = Process()
        process.environment = config.execConfig.envVariables
        process.executableURL = URL(filePath: "/bin/bash")
        process.arguments = ["-c", args.bashScript]
        return Result { try process.run() }.isSuccess
    }
}

```

## /Sources/AppBundle/command/impl/FlattenWorkspaceTreeCommand.swift

```swift path="/Sources/AppBundle/command/impl/FlattenWorkspaceTreeCommand.swift" 
import AppKit
import Common

struct FlattenWorkspaceTreeCommand: Command {
    let args: FlattenWorkspaceTreeCmdArgs
    /*conforms*/ let shouldResetClosedWindowsCache: Bool = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        let workspace = target.workspace
        let windows = workspace.rootTilingContainer.allLeafWindowsRecursive
        for window in windows {
            window.bind(to: workspace.rootTilingContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/FocusBackAndForthCommand.swift

```swift path="/Sources/AppBundle/command/impl/FocusBackAndForthCommand.swift" 
import AppKit
import Common

struct FocusBackAndForthCommand: Command {
    let args: FocusBackAndForthCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        if let prevFocus {
            return setFocus(to: prevFocus)
        } else {
            return io.err("Prev window has been closed")
        }
    }
}

```

## /Sources/AppBundle/command/impl/FocusCommand.swift

```swift path="/Sources/AppBundle/command/impl/FocusCommand.swift" 
import AppKit
import Common

struct FocusCommand: Command {
    let args: FocusCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        // todo bug: floating windows break mru
        let floatingWindows = args.floatingAsTiling ? try await makeFloatingWindowsSeenAsTiling(workspace: target.workspace) : []
        defer {
            if args.floatingAsTiling {
                restoreFloatingWindows(floatingWindows: floatingWindows, workspace: target.workspace)
            }
        }

        switch args.target {
            case .direction(let direction):
                let window = target.windowOrNil
                if let (parent, ownIndex) = window?.closestParent(hasChildrenInDirection: direction, withLayout: nil) {
                    guard let windowToFocus = parent.children[ownIndex + direction.focusOffset]
                        .findLeafWindowRecursive(snappedTo: direction.opposite) else { return false }
                    return windowToFocus.focusWindow()
                } else {
                    return hitWorkspaceBoundaries(target, io, args, direction)
                }
            case .windowId(let windowId):
                if let windowToFocus = Window.get(byId: windowId) {
                    return windowToFocus.focusWindow()
                } else {
                    return io.err("Can't find window with ID \(windowId)")
                }
            case .dfsIndex(let dfsIndex):
                if let windowToFocus = target.workspace.rootTilingContainer.allLeafWindowsRecursive.getOrNil(atIndex: Int(dfsIndex)) {
                    return windowToFocus.focusWindow()
                } else {
                    return io.err("Can't find window with DFS index \(dfsIndex)")
                }
            case .dfsRelative(let nextPrev):
                let windows = target.workspace.rootTilingContainer.allLeafWindowsRecursive
                guard let currentIndex = windows.firstIndex(where: { $0 == target.windowOrNil }) else {
                    return false
                }
                var targetIndex = switch nextPrev {
                    case .dfsNext: currentIndex + 1
                    case .dfsPrev: currentIndex - 1
                }
                if !(0 ..< windows.count).contains(targetIndex) {
                    switch args.boundariesAction {
                        case .stop: return true
                        case .fail: return false
                        case .wrapAroundTheWorkspace: targetIndex = (targetIndex + windows.count) % windows.count
                        case .wrapAroundAllMonitors: return dieT("Must be discarded by args parser")
                    }
                }
                return windows[targetIndex].focusWindow()
        }
    }
}

@MainActor private func hitWorkspaceBoundaries(
    _ target: LiveFocus,
    _ io: CmdIo,
    _ args: FocusCmdArgs,
    _ direction: CardinalDirection,
) -> Bool {
    switch args.boundaries {
        case .workspace:
            return switch args.boundariesAction {
                case .stop: true
                case .fail: false
                case .wrapAroundTheWorkspace: wrapAroundTheWorkspace(target, io, direction)
                case .wrapAroundAllMonitors: dieT("Must be discarded by args parser")
            }
        case .allMonitorsOuterFrame:
            let currentMonitor = target.workspace.workspaceMonitor
            guard let (monitors, index) = currentMonitor.findRelativeMonitor(inDirection: direction) else {
                return io.err("Should never happen. Can't find the current monitor")
            }

            if let targetMonitor = monitors.getOrNil(atIndex: index) {
                return targetMonitor.activeWorkspace.focusWorkspace()
            } else {
                guard let wrapped = monitors.get(wrappingIndex: index) else { return false }
                return hitAllMonitorsOuterFrameBoundaries(target, io, args, direction, wrapped)
            }
    }
}

@MainActor private func hitAllMonitorsOuterFrameBoundaries(
    _ target: LiveFocus,
    _ io: CmdIo,
    _ args: FocusCmdArgs,
    _ direction: CardinalDirection,
    _ wrappedMonitor: Monitor,
) -> Bool {
    switch args.boundariesAction {
        case .stop:
            return true
        case .fail:
            return false
        case .wrapAroundTheWorkspace:
            return wrapAroundTheWorkspace(target, io, direction)
        case .wrapAroundAllMonitors:
            wrappedMonitor.activeWorkspace.findLeafWindowRecursive(snappedTo: direction.opposite)?.markAsMostRecentChild()
            return wrappedMonitor.activeWorkspace.focusWorkspace()
    }
}

@MainActor private func wrapAroundTheWorkspace(_ target: LiveFocus, _ io: CmdIo, _ direction: CardinalDirection) -> Bool {
    guard let windowToFocus = target.workspace.findLeafWindowRecursive(snappedTo: direction.opposite) else {
        return io.err(noWindowIsFocused)
    }
    return windowToFocus.focusWindow()
}

@MainActor private func makeFloatingWindowsSeenAsTiling(workspace: Workspace) async throws -> [FloatingWindowData] {
    let mruBefore = workspace.mostRecentWindowRecursive
    defer {
        mruBefore?.markAsMostRecentChild()
    }
    var _floatingWindows: [FloatingWindowData] = []
    for window in workspace.floatingWindows {
        let center = try await window.getCenter() // todo bug: we shouldn't access ax api here. What if the window was moved but it wasn't committed to ax yet?
        guard let center else { continue }
        // todo bug: what if there are no tiling windows on the workspace?
        guard let target = center.coerceIn(rect: workspace.workspaceMonitor.visibleRectPaddedByOuterGaps)?
            .findIn(tree: workspace.rootTilingContainer, virtual: true) else { continue }
        guard let targetCenter = try await target.getCenter() else { continue }
        guard let tilingParent = target.parent as? TilingContainer else { continue }
        let index = center.getProjection(tilingParent.orientation) >= targetCenter.getProjection(tilingParent.orientation)
            ? target.ownIndex.orDie() + 1
            : target.ownIndex.orDie()
        let data = window.unbindFromParent()
        _floatingWindows.append(FloatingWindowData(window: window, center: center, parent: tilingParent, adaptiveWeight: data.adaptiveWeight, index: index))
    }
    let floatingWindows: [FloatingWindowData] = _floatingWindows.sortedBy { $0.center.getProjection($0.parent.orientation) }.reversed()

    for floating in floatingWindows { // Make floating windows be seen as tiling
        floating.window.bind(to: floating.parent, adaptiveWeight: 1, index: floating.index)
    }
    return floatingWindows
}

@MainActor private func restoreFloatingWindows(floatingWindows: [FloatingWindowData], workspace: Workspace) {
    let mruBefore = workspace.mostRecentWindowRecursive
    defer {
        mruBefore?.markAsMostRecentChild()
    }
    for floating in floatingWindows {
        floating.window.bind(to: workspace, adaptiveWeight: floating.adaptiveWeight, index: INDEX_BIND_LAST)
    }
}

private struct FloatingWindowData {
    let window: Window
    let center: CGPoint

    let parent: TilingContainer
    let adaptiveWeight: CGFloat
    let index: Int
}

extension TreeNode {
    @MainActor
    func findLeafWindowRecursive(snappedTo direction: CardinalDirection) -> Window? {
        switch nodeCases {
            case .workspace(let workspace):
                return workspace.rootTilingContainer.findLeafWindowRecursive(snappedTo: direction)
            case .window(let window):
                return window
            case .tilingContainer(let container):
                if direction.orientation == container.orientation {
                    return (direction.isPositive ? container.children.last : container.children.first)?
                        .findLeafWindowRecursive(snappedTo: direction)
                } else {
                    return mostRecentChild?.findLeafWindowRecursive(snappedTo: direction)
                }
            case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer,
                 .macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer:
                die("Impossible")
        }
    }
}

```

## /Sources/AppBundle/command/impl/FocusMonitorCommand.swift

```swift path="/Sources/AppBundle/command/impl/FocusMonitorCommand.swift" 
import AppKit
import Common

struct FocusMonitorCommand: Command {
    let args: FocusMonitorCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        return switch args.target.val.resolve(target.workspace.workspaceMonitor, wrapAround: args.wrapAround) {
            case .success(let targetMonitor): targetMonitor.activeWorkspace.focusWorkspace()
            case .failure(let msg): io.err(msg)
        }
    }
}

extension MonitorTarget {
    func resolve(_ currentMonitor: Monitor, wrapAround: Bool) -> Result<Monitor, String> {
        switch self {
            case .direction(let direction):
                guard let (monitorsInDirection, index) = currentMonitor.findRelativeMonitor(inDirection: direction) else {
                    return .failure("Should never happen. Can't find the current monitor")
                }
                let targetMonitor = wrapAround ? monitorsInDirection.get(wrappingIndex: index) : monitorsInDirection.getOrNil(atIndex: index)
                guard let targetMonitor else {
                    return .failure("No monitors in direction \(direction)")
                }
                return .success(targetMonitor)
            case .relative(let nextPrev):
                let monitors = sortedMonitors
                guard let curIndex = monitors.firstIndex(where: { $0.rect.topLeftCorner == currentMonitor.rect.topLeftCorner }) else {
                    return .failure("Can't find current monitor")
                }
                let targetIndex = nextPrev == .next ? curIndex + 1 : curIndex - 1
                let targetMonitor = wrapAround ? monitors.get(wrappingIndex: targetIndex) : monitors.getOrNil(atIndex: targetIndex)
                guard let targetMonitor else {
                    return .failure("Can't find target monitor")
                }
                return .success(targetMonitor)
            case .patterns(let patterns):
                let monitors = sortedMonitors
                guard let targetMonitor = patterns.lazy.compactMap({ $0.resolveMonitor(sortedMonitors: monitors) }).first else {
                    return .failure("None of the monitors match the pattern(s)")
                }
                return .success(targetMonitor)
        }
    }
}

extension Monitor {
    func relation(to monitor: Monitor) -> Orientation {
        guard let otherYRange = monitor.rect.minY.until(excl: monitor.rect.maxY) else { return .h }
        guard let myYRange = rect.minY.until(excl: rect.maxY) else { return .h }
        return myYRange.overlaps(otherYRange) ? .h : .v
    }

    func findRelativeMonitor(inDirection direction: CardinalDirection) -> (monitorsInDirection: [Monitor], index: Int)? {
        let currentMonitor = self
        let monitors = sortedMonitors.filter {
            currentMonitor.rect.topLeftCorner == $0.rect.topLeftCorner ||
                $0.relation(to: currentMonitor) == direction.orientation
        }
        guard let index = monitors.firstIndex(where: { $0.rect.topLeftCorner == currentMonitor.rect.topLeftCorner }) else { return nil }
        return (monitors, index + direction.focusOffset)
    }
}

```

## /Sources/AppBundle/command/impl/FullscreenCommand.swift

```swift path="/Sources/AppBundle/command/impl/FullscreenCommand.swift" 
import AppKit
import Common

struct FullscreenCommand: Command {
    let args: FullscreenCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        let newState: Bool = switch args.toggle {
            case .on: true
            case .off: false
            case .toggle: !window.isFullscreen
        }
        if newState == window.isFullscreen {
            io.err((newState ? "Already fullscreen. " : "Already not fullscreen. ") +
                "Tip: use --fail-if-noop to exit with non-zero code")
            return !args.failIfNoop
        }
        window.isFullscreen = newState
        window.noOuterGapsInFullscreen = args.noOuterGaps

        // Focus on its own workspace
        window.markAsMostRecentChild()
        return true
    }
}

let noWindowIsFocused = "No window is focused"

```

## /Sources/AppBundle/command/impl/JoinWithCommand.swift

```swift path="/Sources/AppBundle/command/impl/JoinWithCommand.swift" 
import AppKit
import Common

struct JoinWithCommand: Command {
    let args: JoinWithCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        let direction = args.direction.val
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let currentWindow = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        guard let (parent, ownIndex) = currentWindow.closestParent(hasChildrenInDirection: direction, withLayout: nil) else {
            return io.err("No windows in the specified direction")
        }
        let joinWithTarget = parent.children[ownIndex + direction.focusOffset]
        let prevBinding = joinWithTarget.unbindFromParent()
        let newParent = TilingContainer(
            parent: parent,
            adaptiveWeight: prevBinding.adaptiveWeight,
            parent.orientation.opposite,
            .tiles,
            index: prevBinding.index,
        )
        currentWindow.unbindFromParent()

        joinWithTarget.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: 0)
        currentWindow.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: direction.isPositive ? 0 : INDEX_BIND_LAST)
        return true
    }
}

```

## /Sources/AppBundle/command/impl/LayoutCommand.swift

```swift path="/Sources/AppBundle/command/impl/LayoutCommand.swift" 
import AppKit
import Common

struct LayoutCommand: Command {
    let args: LayoutCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        let targetDescription = args.toggleBetween.val.first(where: { !window.matchesDescription($0) })
            ?? args.toggleBetween.val.first.orDie()
        if window.matchesDescription(targetDescription) { return false }
        switch targetDescription {
            case .h_accordion:
                return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: .h, window: window)
            case .v_accordion:
                return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: .v, window: window)
            case .h_tiles:
                return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: .h, window: window)
            case .v_tiles:
                return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: .v, window: window)
            case .accordion:
                return changeTilingLayout(io, targetLayout: .accordion, targetOrientation: nil, window: window)
            case .tiles:
                return changeTilingLayout(io, targetLayout: .tiles, targetOrientation: nil, window: window)
            case .horizontal:
                return changeTilingLayout(io, targetLayout: nil, targetOrientation: .h, window: window)
            case .vertical:
                return changeTilingLayout(io, targetLayout: nil, targetOrientation: .v, window: window)
            case .tiling:
                guard let parent = window.parent else { return false }
                switch parent.cases {
                    case .macosPopupWindowsContainer:
                        return false // Impossible
                    case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
                        return io.err("Can't change layout for macOS minimized, fullscreen windows or windows or hidden apps. This behavior is subject to change")
                    case .tilingContainer:
                        return true // Nothing to do
                    case .workspace(let workspace):
                        window.lastFloatingSize = try await window.getAxSize() ?? window.lastFloatingSize
                        try await window.relayoutWindow(on: workspace, forceTile: true)
                        return true
                }
            case .floating:
                let workspace = target.workspace
                window.bindAsFloatingWindow(to: workspace)
                if let size = window.lastFloatingSize { window.setSizeAsync(size) }
                return true
        }
    }
}

@MainActor private func changeTilingLayout(_ io: CmdIo, targetLayout: Layout?, targetOrientation: Orientation?, window: Window) -> Bool {
    guard let parent = window.parent else { return false }
    switch parent.cases {
        case .tilingContainer(let parent):
            let targetOrientation = targetOrientation ?? parent.orientation
            let targetLayout = targetLayout ?? parent.layout
            parent.layout = targetLayout
            parent.changeOrientation(targetOrientation)
            return true
        case .workspace, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer,
             .macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer:
            return io.err("The window is non-tiling")
    }
}

extension Window {
    fileprivate func matchesDescription(_ layout: LayoutCmdArgs.LayoutDescription) -> Bool {
        return switch layout {
            case .accordion:   (parent as? TilingContainer)?.layout == .accordion
            case .tiles:       (parent as? TilingContainer)?.layout == .tiles
            case .horizontal:  (parent as? TilingContainer)?.orientation == .h
            case .vertical:    (parent as? TilingContainer)?.orientation == .v
            case .h_accordion: (parent as? TilingContainer).map { $0.layout == .accordion && $0.orientation == .h } == true
            case .v_accordion: (parent as? TilingContainer).map { $0.layout == .accordion && $0.orientation == .v } == true
            case .h_tiles:     (parent as? TilingContainer).map { $0.layout == .tiles && $0.orientation == .h } == true
            case .v_tiles:     (parent as? TilingContainer).map { $0.layout == .tiles && $0.orientation == .v } == true
            case .tiling:      parent is TilingContainer
            case .floating:    parent is Workspace
        }
    }
}

```

## /Sources/AppBundle/command/impl/ListAppsCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListAppsCommand.swift" 
import AppKit
import Common

struct ListAppsCommand: Command {
    let args: ListAppsCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        var result = Array(MacApp.allAppsMap.values)
        if let hidden = args.macosHidden {
            result = result.filter { $0.nsApp.isHidden == hidden }
        }

        if args.outputOnlyCount {
            return io.out("\(result.count)")
        } else {
            let list = result.map { AeroObj.app($0) }
            if args.json {
                return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
                    case .success(let json): io.out(json)
                    case .failure(let msg): io.err(msg)
                }
            } else {
                return switch list.format(args.format) {
                    case .success(let lines): io.out(lines)
                    case .failure(let msg): io.err(msg)
                }
            }
        }
    }
}

```

## /Sources/AppBundle/command/impl/ListExecEnvVarsCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListExecEnvVarsCommand.swift" 
import AppKit
import Common

struct ListExecEnvVarsCommand: Command {
    let args: ListExecEnvVarsCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        for (key, value) in config.execConfig.envVariables {
            io.out("\(key)=\(value)")
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/ListModesCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListModesCommand.swift" 
import AppKit
import Common

struct ListModesCommand: Command {
    let args: ListModesCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        let modes: [String] = args.current ? [activeMode ?? mainModeId] : config.modes.keys.sorted()
        return switch true {
            case args.outputOnlyCount:
                io.out("\(modes.count)")
            case args.json:
                JSONEncoder.aeroSpaceDefault.encodeToString(modes.map { ["mode-id": $0] }).map(io.out)
                    ?? io.err("Failed to encode JSON")
            default:
                io.out(modes)
        }
    }
}

```

## /Sources/AppBundle/command/impl/ListMonitorsCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListMonitorsCommand.swift" 
import AppKit
import Common

struct ListMonitorsCommand: Command {
    let args: ListMonitorsCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        let focus = focus
        var result = sortedMonitors
        if let focused = args.focused {
            result = result.filter { (monitor) in (monitor.activeWorkspace == focus.workspace) == focused }
        }
        if let mouse = args.mouse {
            let mouseWorkspace = mouseLocation.monitorApproximation.activeWorkspace
            result = result.filter { (monitor) in (monitor.activeWorkspace == mouseWorkspace) == mouse }
        }

        if args.outputOnlyCount {
            return io.out("\(result.count)")
        } else {
            let list = result.map { AeroObj.monitor($0) }
            if args.json {
                return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
                    case .success(let json): io.out(json)
                    case .failure(let msg): io.err(msg)
                }
            } else {
                return switch list.format(args.format) {
                    case .success(let lines): io.out(lines)
                    case .failure(let msg): io.err(msg)
                }
            }
        }
    }
}

```

## /Sources/AppBundle/command/impl/ListWindowsCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListWindowsCommand.swift" 
import AppKit
import Common

struct ListWindowsCommand: Command {
    let args: ListWindowsCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        let focus = focus
        var windows: [Window] = []

        if args.filteringOptions.focused {
            if let window = focus.windowOrNil {
                windows = [window]
            } else {
                return io.err(noWindowIsFocused)
            }
        } else {
            var workspaces: Set<Workspace> = args.filteringOptions.workspaces.isEmpty
                ? Workspace.all.toSet()
                : args.filteringOptions.workspaces
                    .flatMap { filter in
                        switch filter {
                            case .focused: [focus.workspace]
                            case .visible: Workspace.all.filter(\.isVisible)
                            case .name(let name): [Workspace.get(byName: name.raw)]
                        }
                    }
                    .toSet()
            if !args.filteringOptions.monitors.isEmpty {
                let monitors: Set<CGPoint> = args.filteringOptions.monitors.resolveMonitors(io)
                if monitors.isEmpty { return false }
                workspaces = workspaces.filter { monitors.contains($0.workspaceMonitor.rect.topLeftCorner) }
            }
            windows = workspaces.flatMap(\.allLeafWindowsRecursive)
            if let pid = args.filteringOptions.pidFilter {
                windows = windows.filter { $0.app.pid == pid }
            }
            if let appId = args.filteringOptions.appIdFilter {
                windows = windows.filter { $0.app.rawAppBundleId == appId }
            }
        }

        if args.outputOnlyCount {
            return io.out("\(windows.count)")
        } else {
            var _list: [(window: Window, title: String)] = [] // todo cleanup
            for window in windows {
                _list.append((window, try await window.title))
            }
            _list = _list.filter { $0.window.isBound }
            _list = _list.sortedBy([{ $0.window.app.name ?? "" }, \.title])

            let list = _list.map { AeroObj.window(window: $0.window, title: $0.title) }
            if args.json {
                return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
                    case .success(let json): io.out(json)
                    case .failure(let msg): io.err(msg)
                }
            } else {
                return switch list.format(args.format) {
                    case .success(let lines): io.out(lines)
                    case .failure(let msg): io.err(msg)
                }
            }
        }
    }
}

```

## /Sources/AppBundle/command/impl/ListWorkspacesCommand.swift

```swift path="/Sources/AppBundle/command/impl/ListWorkspacesCommand.swift" 
import AppKit
import Common

struct ListWorkspacesCommand: Command {
    let args: ListWorkspacesCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        var result: [Workspace] = Workspace.all
        if let visible = args.filteringOptions.visible {
            result = result.filter { $0.isVisible == visible }
        }
        if !args.filteringOptions.onMonitors.isEmpty {
            let monitors: Set<CGPoint> = args.filteringOptions.onMonitors.resolveMonitors(io)
            if monitors.isEmpty { return false }
            result = result.filter { monitors.contains($0.workspaceMonitor.rect.topLeftCorner) }
        }
        if let empty = args.filteringOptions.empty {
            result = result.filter { $0.isEffectivelyEmpty == empty }
        }

        if args.outputOnlyCount {
            return io.out("\(result.count)")
        } else {
            let list = result.map { AeroObj.workspace($0) }
            if args.json {
                return switch list.formatToJson(args.format, ignoreRightPaddingVar: args._format.isEmpty) {
                    case .success(let json): io.out(json)
                    case .failure(let msg): io.err(msg)
                }
            } else {
                return switch list.format(args.format) {
                    case .success(let lines): io.out(lines)
                    case .failure(let msg): io.err(msg)
                }
            }
        }
    }
}

extension [MonitorId] {
    @MainActor func resolveMonitors(_ io: CmdIo) -> Set<CGPoint> {
        var requested: Set<CGPoint> = []
        let sortedMonitors = sortedMonitors
        for id in self {
            let resolved = id.resolve(io, sortedMonitors: sortedMonitors)
            if resolved.isEmpty {
                return []
            }
            for monitor in resolved {
                requested.insert(monitor.rect.topLeftCorner)
            }
        }
        return requested
    }
}

extension MonitorId {
    @MainActor func resolve(_ io: CmdIo, sortedMonitors: [Monitor]) -> [Monitor] {
        switch self {
            case .focused:
                return [focus.workspace.workspaceMonitor]
            case .mouse:
                return [mouseLocation.monitorApproximation]
            case .all:
                return monitors
            case .index(let index):
                if let monitor = sortedMonitors.getOrNil(atIndex: index) {
                    return [monitor]
                } else {
                    io.err("Invalid monitor ID: \(index + 1)")
                    return []
                }
        }
    }
}

```

## /Sources/AppBundle/command/impl/MacosNativeFullscreenCommand.swift

```swift path="/Sources/AppBundle/command/impl/MacosNativeFullscreenCommand.swift" 
import AppKit
import Common

/// Problem ID-B6E178F2: It's not first-class citizen command in AeroSpace model, since it interacts with macOS API directly.
/// Consecutive macos-native-fullscreen commands may not works as expected (because macOS may report correct state with a
/// delay), or may flicker
///
/// The same applies to macos-native-minimize command
struct MacosNativeFullscreenCommand: Command {
    let args: MacosNativeFullscreenCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        let prevState = try await window.isMacosFullscreen
        let newState: Bool = switch args.toggle {
            case .on: true
            case .off: false
            case .toggle: !prevState
        }
        if newState == prevState {
            io.err((newState ? "Already fullscreen. " : "Already not fullscreen. ") +
                "Tip: use --fail-if-noop to exit with non-zero exit code")
            return !args.failIfNoop
        }
        window.asMacWindow().setNativeFullscreen(newState)
        guard let workspace = window.visualWorkspace else {
            return io.err(windowIsntPartOfTree(window))
        }
        if newState { // Enter fullscreen
            window.bind(to: workspace.macOsNativeFullscreenWindowsContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
        } else { // Exit fullscreen
            switch window.layoutReason {
                case .macos(let prevParentKind):
                    try await exitMacOsNativeUnconventionalState(window: window, prevParentKind: prevParentKind, workspace: workspace)
                default:
                    try await window.relayoutWindow(on: workspace)
            }
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/MacosNativeMinimizeCommand.swift

```swift path="/Sources/AppBundle/command/impl/MacosNativeMinimizeCommand.swift" 
import AppKit
import Common

/// See: MacosNativeFullscreenCommand. Problem ID-B6E178F2
struct MacosNativeMinimizeCommand: Command {
    let args: MacosNativeMinimizeCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        // resolveTargetOrReportError on already minimized windows will always fail
        // It would be easier if minimized windows were part of the workspace in tree hierarchy
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        let newState: Bool = try await !window.isMacosMinimized
        window.asMacWindow().setNativeMinimized(newState)
        if newState { // minimize
            window.bind(to: macosMinimizedWindowsContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST)
            return true
        } else { // unminimize
            return io.err("The command is uncapable of unminimizing windows yet. Sorry") // dead code. should never be possible, see the comment above
        }
    }
}

```

## /Sources/AppBundle/command/impl/ModeCommand.swift

```swift path="/Sources/AppBundle/command/impl/ModeCommand.swift" 
import AppKit
import Common

struct ModeCommand: Command {
    let args: ModeCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        activateMode(args.targetMode.val)
        return true
    }
}

```

## /Sources/AppBundle/command/impl/MoveCommand.swift

```swift path="/Sources/AppBundle/command/impl/MoveCommand.swift" 
import AppKit
import Common

struct MoveCommand: Command {
    let args: MoveCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        let direction = args.direction.val
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let currentWindow = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        guard let parent = currentWindow.parent else { return false }
        switch parent.cases {
            case .tilingContainer(let parent):
                let indexOfCurrent = currentWindow.ownIndex.orDie()
                let indexOfSiblingTarget = indexOfCurrent + direction.focusOffset
                if parent.orientation == direction.orientation && parent.children.indices.contains(indexOfSiblingTarget) {
                    switch parent.children[indexOfSiblingTarget].tilingTreeNodeCasesOrDie() {
                        case .tilingContainer(let topLevelSiblingTargetContainer):
                            return deepMoveIn(window: currentWindow, into: topLevelSiblingTargetContainer, moveDirection: direction)
                        case .window: // "swap windows"
                            let prevBinding = currentWindow.unbindFromParent()
                            currentWindow.bind(to: parent, adaptiveWeight: prevBinding.adaptiveWeight, index: indexOfSiblingTarget)
                            return true
                    }
                } else {
                    return moveOut(window: currentWindow, direction: direction, io, args, env)
                }
            case .workspace: // floating window
                return io.err("moving floating windows isn't yet supported") // todo
            case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
                return io.err(moveOutMacosUnconventionalWindow)
            case .macosPopupWindowsContainer:
                return false // Impossible
        }
    }
}

@MainActor private func hitWorkspaceBoundaries(
    _ window: Window,
    _ workspace: Workspace,
    _ io: CmdIo,
    _ args: MoveCmdArgs,
    _ direction: CardinalDirection,
    _ env: CmdEnv,
) -> Bool {
    switch args.boundaries {
        case .workspace:
            switch args.boundariesAction {
                case .stop: return true
                case .fail: return false
                case .createImplicitContainer:
                    createImplicitContainerAndMoveWindow(window, workspace, direction)
                    return true
            }
        case .allMonitorsOuterFrame:
            guard let (monitors, index) = window.nodeMonitor?.findRelativeMonitor(inDirection: direction) else {
                return io.err("Should never happen. Can't find the current monitor")
            }

            if monitors.indices.contains(index) {
                let moveNodeToMonitorArgs = MoveNodeToMonitorCmdArgs(target: .direction(direction))
                    .copy(\.windowId, window.windowId)
                    .copy(\.focusFollowsWindow, focus.windowOrNil == window)

                return MoveNodeToMonitorCommand(args: moveNodeToMonitorArgs).run(env, io)
            } else {
                return hitAllMonitorsOuterFrameBoundaries(window, workspace, io, args, direction)
            }
    }
}

@MainActor private func hitAllMonitorsOuterFrameBoundaries(
    _ window: Window,
    _ workspace: Workspace,
    _ io: CmdIo,
    _ args: MoveCmdArgs,
    _ direction: CardinalDirection,
) -> Bool {
    switch args.boundariesAction {
        case .stop: return true
        case .fail: return false
        case .createImplicitContainer:
            createImplicitContainerAndMoveWindow(window, workspace, direction)
            return true
    }
}

private let moveOutMacosUnconventionalWindow = "moving macOS fullscreen, minimized windows and windows of hidden apps isn't yet supported. This behavior is subject to change"

@MainActor private func moveOut(
    window: Window,
    direction: CardinalDirection,
    _ io: CmdIo,
    _ args: MoveCmdArgs,
    _ env: CmdEnv,
) -> Bool {
    let innerMostChild = window.parents.first(where: {
        return switch $0.parent?.cases {
            case .tilingContainer(let parent): parent.orientation == direction.orientation
            // Stop searching
            case .workspace, .macosMinimizedWindowsContainer, nil, .macosFullscreenWindowsContainer,
                 .macosHiddenAppsWindowsContainer, .macosPopupWindowsContainer: true
        }
    }) as? TilingContainer
    guard let innerMostChild else { return false }
    guard let parent = innerMostChild.parent else { return false }
    switch parent.nodeCases {
        case .tilingContainer(let parent):
            check(parent.orientation == direction.orientation)
            guard let ownIndex = innerMostChild.ownIndex else { return false }
            window.bind(to: parent, adaptiveWeight: WEIGHT_AUTO, index: ownIndex + direction.insertionOffset)
            return true
        case .workspace(let parent):
            return hitWorkspaceBoundaries(window, parent, io, args, direction, env)
        case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
            return io.err(moveOutMacosUnconventionalWindow)
        case .macosPopupWindowsContainer:
            return false // Impossible
        case .window:
            die("Window can't contain children nodes")
    }
}

@MainActor private func createImplicitContainerAndMoveWindow(
    _ window: Window,
    _ workspace: Workspace,
    _ direction: CardinalDirection,
) {
    let prevRoot = workspace.rootTilingContainer
    prevRoot.unbindFromParent()
    // Force tiles layout
    _ = TilingContainer(parent: workspace, adaptiveWeight: WEIGHT_AUTO, direction.orientation, .tiles, index: 0)
    check(prevRoot != workspace.rootTilingContainer)
    prevRoot.bind(to: workspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: 0)
    window.bind(to: workspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: direction.insertionOffset)
}

@MainActor private func deepMoveIn(window: Window, into container: TilingContainer, moveDirection: CardinalDirection) -> Bool {
    let deepTarget = container.tilingTreeNodeCasesOrDie().findDeepMoveInTargetRecursive(moveDirection.orientation)
    switch deepTarget {
        case .tilingContainer(let deepTarget):
            window.bind(to: deepTarget, adaptiveWeight: WEIGHT_AUTO, index: 0)
        case .window(let deepTarget):
            guard let parent = deepTarget.parent as? TilingContainer else { return false }
            window.bind(
                to: parent,
                adaptiveWeight: WEIGHT_AUTO,
                index: deepTarget.ownIndex.orDie() + 1,
            )
    }
    return true
}

extension TilingTreeNodeCases {
    @MainActor fileprivate func findDeepMoveInTargetRecursive(_ orientation: Orientation) -> TilingTreeNodeCases {
        return switch self {
            case .window:
                self
            case .tilingContainer(let container):
                if container.orientation == orientation {
                    .tilingContainer(container)
                } else {
                    container.mostRecentChild.orDie("Empty containers must be detached during normalization")
                        .tilingTreeNodeCasesOrDie()
                        .findDeepMoveInTargetRecursive(orientation)
                }
        }
    }
}

```

## /Sources/AppBundle/command/impl/MoveMouseCommand.swift

```swift path="/Sources/AppBundle/command/impl/MoveMouseCommand.swift" 
import AppKit
import Common

struct MoveMouseCommand: Command {
    let args: MoveMouseCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        let mouse = mouseLocation
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        switch args.mouseTarget.val {
            case .windowLazyCenter:
                guard let rect = try await windowSubjectRectOrReportError(target, io) else { return false }
                if rect.contains(mouse) {
                    io.err("The mouse already belongs to the window. Tip: use --fail-if-noop to exit with non-zero code")
                    return !args.failIfNoop
                }
                return moveMouse(io, rect.center)
            case .windowForceCenter:
                guard let rect = try await windowSubjectRectOrReportError(target, io) else { return false }
                return moveMouse(io, rect.center)
            case .monitorLazyCenter:
                let rect = target.workspace.workspaceMonitor.rect
                if rect.contains(mouse) {
                    io.err("The mouse already belongs to the monitor. Tip: use --fail-if-noop to exit with non-zero code")
                    return !args.failIfNoop
                }
                return moveMouse(io, rect.center)
            case .monitorForceCenter:
                return moveMouse(io, target.workspace.workspaceMonitor.rect.center)
        }
    }
}

private func moveMouse(_ io: CmdIo, _ point: CGPoint) -> Bool {
    let event = CGEvent(
        mouseEventSource: nil,
        mouseType: CGEventType.mouseMoved,
        mouseCursorPosition: point,
        mouseButton: CGMouseButton.left,
    )
    if let event {
        event.post(tap: CGEventTapLocation.cghidEventTap)
        return true
    } else {
        return io.err("Failed to move mouse")
    }
}

@MainActor
private func windowSubjectRectOrReportError(_ target: LiveFocus, _ io: CmdIo) async throws -> Rect? {
    // todo bug it's bad that we operate on the "ax physical" state directly. command seq won't work correctly
    //      focus <direction> command has the similar problem
    if let window: Window = target.windowOrNil {
        if let rect = window.lastAppliedLayoutPhysicalRect {
            return rect
        } else if let rect = try await window.getAxRect() {
            return rect
        } else {
            io.err("Failed to get rect of window '\(window.windowId)'")
            return nil
        }
    } else {
        io.err(noWindowIsFocused)
        return nil
    }
}

```

## /Sources/AppBundle/command/impl/MoveNodeToMonitorCommand.swift

```swift path="/Sources/AppBundle/command/impl/MoveNodeToMonitorCommand.swift" 
import AppKit
import Common

struct MoveNodeToMonitorCommand: Command {
    let args: MoveNodeToMonitorCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        guard let currentMonitor = window.nodeMonitor else {
            return io.err(windowIsntPartOfTree(window))
        }
        switch args.target.val.resolve(currentMonitor, wrapAround: args.wrapAround) {
            case .success(let targetMonitor):
                let targetWs = targetMonitor.activeWorkspace
                let index = true == args.target.val.directionOrNil
                    .map { dir in dir.isPositive && targetWs.rootTilingContainer.orientation == dir.orientation }
                    ? 0
                    : INDEX_BIND_LAST
                return moveWindowToWorkspace(
                    window,
                    targetWs,
                    io,
                    focusFollowsWindow: args.focusFollowsWindow,
                    failIfNoop: args.failIfNoop,
                    index: index,
                )
            case .failure(let msg):
                return io.err(msg)
        }
    }
}

func windowIsntPartOfTree(_ window: Window) -> String {
    "Window \(window.windowId) is not part of tree (minimized or hidden)"
}

```

## /Sources/AppBundle/command/impl/MoveNodeToWorkspaceCommand.swift

```swift path="/Sources/AppBundle/command/impl/MoveNodeToWorkspaceCommand.swift" 
import Common

struct MoveNodeToWorkspaceCommand: Command {
    let args: MoveNodeToWorkspaceCmdArgs
    /*conforms*/ let shouldResetClosedWindowsCache: Bool = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else { return io.err(noWindowIsFocused) }
        let subjectWs = window.nodeWorkspace
        let targetWorkspace: Workspace
        switch args.target.val {
            case .relative(let nextPrev):
                guard let subjectWs else { return io.err("Window \(window.windowId) doesn't belong to any workspace") }
                let ws = getNextPrevWorkspace(
                    current: subjectWs,
                    isNext: nextPrev == .next,
                    wrapAround: args.wrapAround,
                    stdin: args.useStdin ? io.readStdin() : nil,
                    target: target,
                )
                guard let ws else { return io.err("Can't resolve next or prev workspace") }
                targetWorkspace = ws
            case .direct(let name):
                targetWorkspace = Workspace.get(byName: name.raw)
        }
        return moveWindowToWorkspace(window, targetWorkspace, io, focusFollowsWindow: args.focusFollowsWindow, failIfNoop: args.failIfNoop)
    }
}

@MainActor
func moveWindowToWorkspace(_ window: Window, _ targetWorkspace: Workspace, _ io: CmdIo, focusFollowsWindow: Bool, failIfNoop: Bool, index: Int = INDEX_BIND_LAST) -> Bool {
    if window.nodeWorkspace == targetWorkspace {
        io.err("Window '\(window.windowId)' already belongs to workspace '\(targetWorkspace.name)'. Tip: use --fail-if-noop to exit with non-zero code")
        return !failIfNoop
    }
    let targetContainer: NonLeafTreeNodeObject = window.isFloating ? targetWorkspace : targetWorkspace.rootTilingContainer
    window.bind(to: targetContainer, adaptiveWeight: WEIGHT_AUTO, index: index)
    return focusFollowsWindow ? window.focusWindow() : true
}

```

## /Sources/AppBundle/command/impl/MoveWorkspaceToMonitorCommand.swift

```swift path="/Sources/AppBundle/command/impl/MoveWorkspaceToMonitorCommand.swift" 
import AppKit
import Common

struct MoveWorkspaceToMonitorCommand: Command {
    let args: MoveWorkspaceToMonitorCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        let focusedWorkspace = target.workspace
        let prevMonitor = focusedWorkspace.workspaceMonitor

        switch args.target.val.resolve(target.workspace.workspaceMonitor, wrapAround: args.wrapAround) {
            case .success(let targetMonitor):
                if targetMonitor.monitorId == prevMonitor.monitorId {
                    return true
                }
                if targetMonitor.setActiveWorkspace(focusedWorkspace) {
                    let stubWorkspace = getStubWorkspace(for: prevMonitor)
                    check(
                        prevMonitor.setActiveWorkspace(stubWorkspace),
                        "getStubWorkspace generated incompatible stub workspace (\(stubWorkspace)) for the monitor (\(prevMonitor)",
                    )
                    return true
                } else {
                    return io.err(
                        "Can't move workspace '\(focusedWorkspace.name)' to monitor '\(targetMonitor.name)'. workspace-to-monitor-force-assignment doesn't allow it",
                    )
                }
            case .failure(let msg):
                return io.err(msg)
        }
    }
}

```

## /Sources/AppBundle/command/impl/ReloadConfigCommand.swift

```swift path="/Sources/AppBundle/command/impl/ReloadConfigCommand.swift" 
import AppKit
import Common

struct ReloadConfigCommand: Command {
    let args: ReloadConfigCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        var stdout = ""
        let isOk = reloadConfig(args: args, stdout: &stdout)
        if !stdout.isEmpty {
            io.out(stdout)
        }
        return isOk
    }
}

@MainActor func reloadConfig(forceConfigUrl: URL? = nil) -> Bool {
    var devNull = ""
    return reloadConfig(forceConfigUrl: forceConfigUrl, stdout: &devNull)
}

@MainActor func reloadConfig(
    args: ReloadConfigCmdArgs = ReloadConfigCmdArgs(rawArgs: []),
    forceConfigUrl: URL? = nil,
    stdout: inout String,
) -> Bool {
    switch readConfig(forceConfigUrl: forceConfigUrl) {
        case .success(let (parsedConfig, url)):
            if !args.dryRun {
                resetHotKeys()
                config = parsedConfig
                configUrl = url
                activateMode(activeMode)
                syncStartAtLogin()
                MessageModel.shared.message = nil
            }
            return true
        case .failure(let msg):
            stdout.append(msg)
            if !args.noGui {
                Task { @MainActor in
                    MessageModel.shared.message = Message(description: "AeroSpace Config Error", body: msg)
                }
            }
            return false
    }
}

```

## /Sources/AppBundle/command/impl/ResizeCommand.swift

```swift path="/Sources/AppBundle/command/impl/ResizeCommand.swift" 
import AppKit
import Common

struct ResizeCommand: Command { // todo cover with tests
    let args: ResizeCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }

        let candidates = target.windowOrNil?.parentsWithSelf
            .filter { ($0.parent as? TilingContainer)?.layout == .tiles }
            ?? []

        let orientation: Orientation?
        let parent: TilingContainer?
        let node: TreeNode?
        switch args.dimension.val {
            case .width:
                orientation = .h
                node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
                parent = node?.parent as? TilingContainer
            case .height:
                orientation = .v
                node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
                parent = node?.parent as? TilingContainer
            case .smart:
                node = candidates.first
                parent = node?.parent as? TilingContainer
                orientation = parent?.orientation
            case .smartOpposite:
                orientation = (candidates.first?.parent as? TilingContainer)?.orientation.opposite
                node = candidates.first(where: { ($0.parent as? TilingContainer)?.orientation == orientation })
                parent = node?.parent as? TilingContainer
        }
        guard let parent else { return io.err("resize command doesn't support floating windows yet https://github.com/nikitabobko/AeroSpace/issues/9") }
        guard let orientation else { return false }
        guard let node else { return false }
        let diff: CGFloat = switch args.units.val {
            case .set(let unit): CGFloat(unit) - node.getWeight(orientation)
            case .add(let unit): CGFloat(unit)
            case .subtract(let unit): -CGFloat(unit)
        }

        guard let childDiff = diff.div(parent.children.count - 1) else { return false }
        parent.children.lazy
            .filter { $0 != node }
            .forEach { $0.setWeight(parent.orientation, $0.getWeight(parent.orientation) - childDiff) }

        node.setWeight(orientation, node.getWeight(orientation) + diff)
        return true
    }
}

```

## /Sources/AppBundle/command/impl/SplitCommand.swift

```swift path="/Sources/AppBundle/command/impl/SplitCommand.swift" 
import AppKit
import Common

struct SplitCommand: Command {
    let args: SplitCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        if config.enableNormalizationFlattenContainers {
            return io.err("'split' has no effect when 'enable-normalization-flatten-containers' normalization enabled. My recommendation: keep the normalizations enabled, and prefer 'join-with' over 'split'.")
        }
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        guard let window = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }
        guard let parent = window.parent else { return false }
        switch parent.cases {
            case .workspace:
                // Nothing to do for floating and macOS native fullscreen windows
                return io.err("Can't split floating windows")
            case .tilingContainer(let parent):
                let orientation: Orientation = switch args.arg.val {
                    case .vertical: .v
                    case .horizontal: .h
                    case .opposite: parent.orientation.opposite
                }
                if parent.children.count == 1 {
                    parent.changeOrientation(orientation)
                } else {
                    let data = window.unbindFromParent()
                    let newParent = TilingContainer(
                        parent: parent,
                        adaptiveWeight: data.adaptiveWeight,
                        orientation,
                        .tiles,
                        index: data.index,
                    )
                    window.bind(to: newParent, adaptiveWeight: WEIGHT_AUTO, index: 0)
                }
                return true
            case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer:
                return io.err("Can't split macos fullscreen, minimized windows and windows of hidden apps. This behavior may change in the future")
            case .macosPopupWindowsContainer:
                return false // Impossible
        }
    }
}

```

## /Sources/AppBundle/command/impl/SummonWorkspaceCommand.swift

```swift path="/Sources/AppBundle/command/impl/SummonWorkspaceCommand.swift" 
import AppKit
import Common

struct SummonWorkspaceCommand: Command {
    let args: SummonWorkspaceCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = true

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        let workspace = Workspace.get(byName: args.target.val.raw)
        let monitor = focus.workspace.workspaceMonitor
        if monitor.activeWorkspace == workspace {
            io.err("Workspace '\(workspace.name)' is already visible on the focused monitor. Tip: use --fail-if-noop to exit with non-zero code")
            return !args.failIfNoop
        }
        if monitor.setActiveWorkspace(workspace) {
            return workspace.focusWorkspace()
        } else {
            return io.err("Can't move workspace '\(workspace.name)' to monitor '\(monitor.name)'. workspace-to-monitor-force-assignment doesn't allow it")
        }
    }
}

```

## /Sources/AppBundle/command/impl/SwapCommand.swift

```swift path="/Sources/AppBundle/command/impl/SwapCommand.swift" 
import AppKit
import Common

struct SwapCommand: Command {
    let args: SwapCmdArgs
    /*conforms*/ let shouldResetClosedWindowsCache: Bool = true

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        guard let target = args.resolveTargetOrReportError(env, io) else {
            return false
        }

        guard let currentWindow = target.windowOrNil else {
            return io.err(noWindowIsFocused)
        }

        let targetWindow: Window?
        switch args.target.val {
            case .direction(let direction):
                if let (parent, ownIndex) = currentWindow.closestParent(hasChildrenInDirection: direction, withLayout: nil) {
                    targetWindow = parent.children[ownIndex + direction.focusOffset].findLeafWindowRecursive(snappedTo: direction.opposite)
                } else if args.wrapAround {
                    targetWindow = target.workspace.findLeafWindowRecursive(snappedTo: direction.opposite)
                } else {
                    return false
                }
            case .dfsRelative(let nextPrev):
                let windows = target.workspace.rootTilingContainer.allLeafWindowsRecursive
                guard let currentIndex = windows.firstIndex(where: { $0 == target.windowOrNil }) else {
                    return false
                }
                var targetIndex = switch nextPrev {
                    case .dfsNext: currentIndex + 1
                    case .dfsPrev: currentIndex - 1
                }
                if !(0 ..< windows.count).contains(targetIndex) {
                    if !args.wrapAround {
                        return false
                    }
                    targetIndex = (targetIndex + windows.count) % windows.count
                }
                targetWindow = windows[targetIndex]
        }

        guard let targetWindow else {
            return false
        }

        swapWindows(currentWindow, targetWindow)

        if args.swapFocus {
            return targetWindow.focusWindow()
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/TriggerBindingCommand.swift

```swift path="/Sources/AppBundle/command/impl/TriggerBindingCommand.swift" 
import AppKit
import Common

struct TriggerBindingCommand: Command {
    let args: TriggerBindingCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool {
        return if let mode = config.modes[args.mode] {
            if let binding = mode.bindings.values.first(where: { $0.descriptionWithKeyNotation == args.binding.val }) {
                // refreshSession is not needed since commands are already run in refreshSession
                try await binding.commands.runCmdSeq(env, io)
            } else {
                io.err("Binding '\(args.binding.val)' is not presented in mode '\(args.mode)'")
            }
        } else {
            io.err("Mode '\(args.mode)' doesn't exist. " +
                "Available modes: \(config.modes.keys.joined(separator: ","))")
        }
    }
}

```

## /Sources/AppBundle/command/impl/VolumeCommand.swift

```swift path="/Sources/AppBundle/command/impl/VolumeCommand.swift" 
import AppKit
import Common
import ISSoundAdditions

struct VolumeCommand: Command {
    let args: VolumeCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        switch args.action.val {
            case .up:
                Sound.output.increaseVolume(by: 0.0625, autoMuteUnmute: true)
            case .down:
                Sound.output.decreaseVolume(by: 0.0625, autoMuteUnmute: true)
            case .muteToggle:
                Sound.output.isMuted.toggle()
            case .muteOn:
                Sound.output.isMuted = true
            case .muteOff:
                Sound.output.isMuted = false
            case .set(let int):
                Sound.output.setVolume(Float(int) / 100, autoMuteUnmute: true)
        }
        if let volume = try? Sound.output.readVolume() {
            VolumePanel.shared.update(with: Sound.output.isMuted ? 0 : volume)
        }
        return true
    }
}

```

## /Sources/AppBundle/command/impl/WorkspaceBackAndForthCommand.swift

```swift path="/Sources/AppBundle/command/impl/WorkspaceBackAndForthCommand.swift" 
import AppKit
import Common

struct WorkspaceBackAndForthCommand: Command {
    let args: WorkspaceBackAndForthCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool {
        return prevFocusedWorkspace?.focusWorkspace() != nil
    }
}

```

## /Sources/AppBundle/command/impl/WorkspaceCommand.swift

```swift path="/Sources/AppBundle/command/impl/WorkspaceCommand.swift" 
import AppKit
import Common
import Foundation

struct WorkspaceCommand: Command {
    let args: WorkspaceCmdArgs
    /*conforms*/ var shouldResetClosedWindowsCache = false

    func run(_ env: CmdEnv, _ io: CmdIo) -> Bool { // todo refactor
        guard let target = args.resolveTargetOrReportError(env, io) else { return false }
        let focusedWs = target.workspace
        let workspaceName: String
        switch args.target.val {
            case .relative(let nextPrev):
                let workspace = getNextPrevWorkspace(
                    current: focusedWs,
                    isNext: nextPrev == .next,
                    wrapAround: args.wrapAround,
                    stdin: args.useStdin ? io.readStdin() : nil,
                    target: target,
                )
                guard let workspace else { return false }
                workspaceName = workspace.name
            case .direct(let name):
                workspaceName = name.raw
                if args.autoBackAndForth && focusedWs.name == workspaceName {
                    return WorkspaceBackAndForthCommand(args: WorkspaceBackAndForthCmdArgs(rawArgs: [])).run(env, io)
                }
        }
        if focusedWs.name == workspaceName {
            io.err("Workspace '\(workspaceName)' is already focused. Tip: use --fail-if-noop to exit with non-zero code")
            return !args.failIfNoop
        } else {
            return Workspace.get(byName: workspaceName).focusWorkspace()
        }
    }
}

@MainActor func getNextPrevWorkspace(current: Workspace, isNext: Bool, wrapAround: Bool, stdin: String?, target: LiveFocus) -> Workspace? {
    let stdinWorkspaces: [String] = stdin?.split(separator: "\n").map { String($0).trim() }.filter { !$0.isEmpty } ?? []
    let currentMonitor = current.workspaceMonitor
    let workspaces: [Workspace] = stdin != nil
        ? stdinWorkspaces.map { Workspace.get(byName: $0) }
        : Workspace.all.filter { $0.workspaceMonitor.rect.topLeftCorner == currentMonitor.rect.topLeftCorner }
            .toSet()
            .union([current])
            .sorted()
    let index = workspaces.firstIndex(where: { $0 == target.workspace }) ?? 0
    let workspace: Workspace? = if wrapAround {
        workspaces.get(wrappingIndex: isNext ? index + 1 : index - 1)
    } else {
        workspaces.getOrNil(atIndex: isNext ? index + 1 : index - 1)
    }
    return workspace
}

```

## /Sources/AppBundle/command/parseCommand.swift

```swift path="/Sources/AppBundle/command/parseCommand.swift" 
import Common
import TOMLKit

func parseCommand(_ raw: String) -> ParsedCmd<any Command> {
    if raw.starts(with: "exec-and-forget") {
        return .cmd(ExecAndForgetCommand(args: ExecAndForgetCmdArgs(bashScript: raw.removePrefix("exec-and-forget"))))
    }
    return switch raw.splitArgs() {
        case .success(let args): parseCommand(args)
        case .failure(let fail): .failure(fail)
    }
}

func parseCommand(_ args: [String]) -> ParsedCmd<any Command> {
    parseCmdArgs(args.slice).map { $0.toCommand() }
}

func expectedActualTypeError(expected: TOMLType, actual: TOMLType) -> String {
    "Expected type is '\(expected)'. But actual type is '\(actual)'"
}

func expectedActualTypeError(expected: [TOMLType], actual: TOMLType) -> String {
    if let single = expected.singleOrNil() {
        return expectedActualTypeError(expected: single, actual: actual)
    } else {
        return "Expected types are \(expected.map { "'\($0.description)'" }.joined(separator: " or ")). But actual type is '\(actual)'"
    }
}

```

## /Sources/AppBundle/config/Config.swift

```swift path="/Sources/AppBundle/config/Config.swift" 
import AppKit
import Common
import HotKey

func getDefaultConfigUrlFromProject() -> URL {
    var url = URL(filePath: #filePath)
    check(FileManager.default.fileExists(atPath: url.path))
    while !FileManager.default.fileExists(atPath: url.appending(component: ".git").path) {
        url.deleteLastPathComponent()
    }
    let projectRoot: URL = url
    return projectRoot.appending(component: "docs/config-examples/default-config.toml")
}

var defaultConfigUrl: URL {
    if isUnitTest {
        return getDefaultConfigUrlFromProject()
    } else {
        return Bundle.main.url(forResource: "default-config", withExtension: "toml")
            // Useful for debug builds that are not app bundles
            ?? getDefaultConfigUrlFromProject()
    }
}
@MainActor let defaultConfig: Config = {
    let parsedConfig = parseConfig((try? String(contentsOf: defaultConfigUrl)).orDie())
    if !parsedConfig.errors.isEmpty {
        die("Can't parse default config: \(parsedConfig.errors)")
    }
    return parsedConfig.config
}()
@MainActor var config: Config = defaultConfig // todo move to Ctx?
@MainActor var configUrl: URL = defaultConfigUrl

struct Config: ConvenienceCopyable {
    var afterLoginCommand: [any Command] = []
    var afterStartupCommand: [any Command] = []
    var _indentForNestedContainersWithTheSameOrientation: Void = ()
    var enableNormalizationFlattenContainers: Bool = true
    var _nonEmptyWorkspacesRootContainersLayoutOnStartup: Void = ()
    var defaultRootContainerLayout: Layout = .tiles
    var defaultRootContainerOrientation: DefaultContainerOrientation = .auto
    var startAtLogin: Bool = false
    var automaticallyUnhideMacosHiddenApps: Bool = false
    var accordionPadding: Int = 30
    var enableNormalizationOppositeOrientationForNestedContainers: Bool = true
    var execOnWorkspaceChange: [String] = [] // todo deprecate
    var keyMapping = KeyMapping()
    var execConfig: ExecConfig = ExecConfig()

    var onFocusChanged: [any Command] = []
    // var onFocusedWorkspaceChanged: [any Command] = []
    var onFocusedMonitorChanged: [any Command] = []

    var gaps: Gaps = .zero
    var workspaceToMonitorForceAssignment: [String: [MonitorDescription]] = [:]
    var modes: [String: Mode] = [:]
    var onWindowDetected: [WindowDetectedCallback] = []

    var preservedWorkspaceNames: [String] = []
}

enum DefaultContainerOrientation: String {
    case horizontal, vertical, auto
}

```

## /Sources/AppBundle/config/ConfigFile.swift

```swift path="/Sources/AppBundle/config/ConfigFile.swift" 
import Common
import Foundation

let configDotfileName = ".aerospace.toml"
func findCustomConfigUrl() -> ConfigFile {
    let xdgConfigHome = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"].map { URL(filePath: $0) }
        ?? FileManager.default.homeDirectoryForCurrentUser.appending(path: ".config/")
    let candidates: [URL] = if let configLocation = serverArgs.configLocation {
        [URL(filePath: configLocation)]
    } else {
        [
            FileManager.default.homeDirectoryForCurrentUser.appending(path: configDotfileName),
            xdgConfigHome.appending(path: "aerospace").appending(path: "aerospace.toml"),
        ]
    }
    let existingCandidates: [URL] = candidates.filter { (candidate: URL) in FileManager.default.fileExists(atPath: candidate.path) }
    let count = existingCandidates.count
    return switch count {
        case 0: .noCustomConfigExists
        case 1: .file(existingCandidates.first.orDie())
        default: .ambiguousConfigError(existingCandidates)
    }
}

enum ConfigFile {
    case file(URL), ambiguousConfigError(_ candidates: [URL]), noCustomConfigExists

    var urlOrNil: URL? {
        return switch self {
            case .file(let url): url
            case .ambiguousConfigError, .noCustomConfigExists: nil
        }
    }
}

```

## /Sources/AppBundle/config/DynamicConfigValue.swift

```swift path="/Sources/AppBundle/config/DynamicConfigValue.swift" 
import Common
import TOMLKit

struct PerMonitorValue<Value: Equatable>: Equatable {
    let description: MonitorDescription
    let value: Value
}
extension PerMonitorValue: Sendable where Value: Sendable {}

enum DynamicConfigValue<Value: Equatable>: Equatable {
    case constant(Value)
    case perMonitor([PerMonitorValue<Value>], default: Value)
}
extension DynamicConfigValue: Sendable where Value: Sendable {}

extension DynamicConfigValue {
    func getValue(for monitor: any Monitor) -> Value {
        switch self {
            case .constant(let value):
                return value
            case .perMonitor(let array, let defaultValue):
                let sortedMonitors = sortedMonitors
                return array
                    .lazy
                    .compactMap {
                        $0.description.resolveMonitor(sortedMonitors: sortedMonitors)?.rect.topLeftCorner == monitor.rect.topLeftCorner
                            ? $0.value
                            : nil
                    }
                    .first ?? defaultValue
        }
    }
}

func parseDynamicValue<T>(
    _ raw: TOMLValueConvertible,
    _ valueType: T.Type,
    _ fallback: T,
    _ backtrace: TomlBacktrace,
    _ errors: inout [TomlParseError],
) -> DynamicConfigValue<T> {
    if let simpleValue = parseSimpleType(raw) as T? {
        return .constant(simpleValue)
    } else if let array = raw.array {
        if array.isEmpty {
            errors.append(.semantic(backtrace, "The array must not be empty"))
            return .constant(fallback)
        }

        guard let defaultValue = array.last.flatMap({ parseSimpleType($0) as T? }) else {
            errors.append(.semantic(backtrace, "The last item in the array must be of type \(T.self)"))
            return .constant(fallback)
        }

        if array.dropLast().isEmpty {
            errors.append(.semantic(backtrace, "The array must contain at least one monitor pattern"))
            return .constant(fallback)
        }

        let rules: [PerMonitorValue<T>] = parsePerMonitorValues(TOMLArray(array.dropLast()), backtrace, &errors)

        return .perMonitor(rules, default: defaultValue)
    } else {
        errors.append(.semantic(backtrace, "Unsupported type: \(raw.type), expected: \(valueType) or array"))
        return .constant(fallback)
    }
}

func parsePerMonitorValues<T>(_ array: TOMLArray, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [PerMonitorValue<T>] {
    array.enumerated().compactMap { (index: Int, raw: TOMLValueConvertible) -> PerMonitorValue<T>? in
        var backtrace = backtrace + .index(index)

        guard let (key, value) = raw.unwrapTableWithSingleKey(expectedKey: "monitor", &backtrace)
            .flatMap({ $0.value.unwrapTableWithSingleKey(expectedKey: nil, &backtrace) })
            .getOrNil(appendErrorTo: &errors)
        else {
            return nil
        }

        let monitorDescriptionResult = parseMonitorDescription(key, backtrace)

        guard let monitorDescription = monitorDescriptionResult.getOrNil(appendErrorTo: &errors) else { return nil }

        guard let value = parseSimpleType(value) as T? else {
            errors.append(.semantic(backtrace, "Expected type is '\(T.self)'. But actual type is '\(value.type)'"))
            return nil
        }

        return PerMonitorValue(description: monitorDescription, value: value)
    }
}

```

## /Sources/AppBundle/config/HotkeyBinding.swift

```swift path="/Sources/AppBundle/config/HotkeyBinding.swift" 
import AppKit
import Common
import Foundation
import HotKey
import TOMLKit

@MainActor private var hotkeys: [String: HotKey] = [:]

@MainActor func resetHotKeys() {
    // Explicitly unregister all hotkeys. We cannot always rely on destruction of the HotKey object to trigger
    // unregistration because we might be running inside a hotkey handler that is keeping its HotKey object alive.
    for (_, key) in hotkeys {
        key.isEnabled = false
    }
    hotkeys = [:]
}

extension HotKey {
    var isEnabled: Bool {
        get { !isPaused }
        set {
            if isEnabled != newValue {
                isPaused = !newValue
            }
        }
    }
}

@MainActor var activeMode: String? = mainModeId
@MainActor func activateMode(_ targetMode: String?) {
    let targetBindings = targetMode.flatMap { config.modes[$0] }?.bindings ?? [:]
    for binding in targetBindings.values where !hotkeys.keys.contains(binding.descriptionWithKeyCode) {
        hotkeys[binding.descriptionWithKeyCode] = HotKey(key: binding.keyCode, modifiers: binding.modifiers, keyDownHandler: {
            Task {
                if let activeMode {
                    try await runSession(.hotkeyBinding, .checkServerIsEnabledOrDie) { () throws in
                        _ = try await config.modes[activeMode]?.bindings[binding.descriptionWithKeyCode]?.commands
                            .runCmdSeq(.defaultEnv, .emptyStdin)
                    }
                }
            }
        })
    }
    for (binding, key) in hotkeys {
        if targetBindings.keys.contains(binding) {
            key.isEnabled = true
        } else {
            key.isEnabled = false
        }
    }
    activeMode = targetMode
}

struct HotkeyBinding: Equatable, Sendable {
    let modifiers: NSEvent.ModifierFlags
    let keyCode: Key
    let commands: [any Command]
    let descriptionWithKeyCode: String
    let descriptionWithKeyNotation: String

    init(_ modifiers: NSEvent.ModifierFlags, _ keyCode: Key, _ commands: [any Command], descriptionWithKeyNotation: String) {
        self.modifiers = modifiers
        self.keyCode = keyCode
        self.commands = commands
        self.descriptionWithKeyCode = modifiers.isEmpty
            ? keyCode.toString()
            : modifiers.toString() + "-" + keyCode.toString()
        self.descriptionWithKeyNotation = descriptionWithKeyNotation
    }

    static func == (lhs: HotkeyBinding, rhs: HotkeyBinding) -> Bool {
        lhs.modifiers == rhs.modifiers &&
            lhs.keyCode == rhs.keyCode &&
            lhs.descriptionWithKeyCode == rhs.descriptionWithKeyCode &&
            zip(lhs.commands, rhs.commands).allSatisfy { $0.equals($1) }
    }
}

func parseBindings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError], _ mapping: [String: Key]) -> [String: HotkeyBinding] {
    guard let rawTable = raw.table else {
        errors += [expectedActualTypeError(expected: .table, actual: raw.type, backtrace)]
        return [:]
    }
    var result: [String: HotkeyBinding] = [:]
    for (binding, rawCommand): (String, TOMLValueConvertible) in rawTable {
        let backtrace = backtrace + .key(binding)
        let binding = parseBinding(binding, backtrace, mapping)
            .flatMap { modifiers, key -> ParsedToml<HotkeyBinding> in
                parseCommandOrCommands(rawCommand).toParsedToml(backtrace).map {
                    HotkeyBinding(modifiers, key, $0, descriptionWithKeyNotation: binding)
                }
            }
            .getOrNil(appendErrorTo: &errors)
        if let binding {
            if result.keys.contains(binding.descriptionWithKeyCode) {
                errors.append(.semantic(backtrace, "'\(binding.descriptionWithKeyCode)' Binding redeclaration"))
            }
            result[binding.descriptionWithKeyCode] = binding
        }
    }
    return result
}

func parseBinding(_ raw: String, _ backtrace: TomlBacktrace, _ mapping: [String: Key]) -> ParsedToml<(NSEvent.ModifierFlags, Key)> {
    let rawKeys = raw.split(separator: "-")
    let modifiers: ParsedToml<NSEvent.ModifierFlags> = rawKeys.dropLast()
        .mapAllOrFailure {
            modifiersMap[String($0)].orFailure(.semantic(backtrace, "Can't parse modifiers in '\(raw)' binding"))
        }
        .map { NSEvent.ModifierFlags($0) }
    let key: ParsedToml<Key> = rawKeys.last.flatMap { mapping[String($0)] }
        .orFailure(.semantic(backtrace, "Can't parse the key in '\(raw)' binding"))
    return modifiers.flatMap { modifiers -> ParsedToml<(NSEvent.ModifierFlags, Key)> in
        key.flatMap { key -> ParsedToml<(NSEvent.ModifierFlags, Key)> in
            .success((modifiers, key))
        }
    }
}

```

## /Sources/AppBundle/config/Mode.swift

```swift path="/Sources/AppBundle/config/Mode.swift" 
import Common
import HotKey
import TOMLKit

struct Mode: ConvenienceCopyable, Equatable, Sendable {
    /// User visible name. Optional. todo drop it?
    var name: String?
    var bindings: [String: HotkeyBinding]

    static let zero = Mode(name: nil, bindings: [:])
}

func parseModes(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError], _ mapping: [String: Key]) -> [String: Mode] {
    guard let rawTable = raw.table else {
        errors += [expectedActualTypeError(expected: .table, actual: raw.type, backtrace)]
        return [:]
    }
    var result: [String: Mode] = [:]
    for (key, value) in rawTable {
        result[key] = parseMode(value, backtrace + .key(key), &errors, mapping)
    }
    if !result.keys.contains(mainModeId) {
        errors += [.semantic(backtrace, "Please specify '\(mainModeId)' mode")]
    }
    return result
}

func parseMode(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError], _ mapping: [String: Key]) -> Mode {
    guard let rawTable: TOMLTable = raw.table else {
        errors += [expectedActualTypeError(expected: .table, actual: raw.type, backtrace)]
        return .zero
    }

    var result: Mode = .zero
    for (key, value) in rawTable {
        let backtrace = backtrace + .key(key)
        switch key {
            case "binding":
                result.bindings = parseBindings(value, backtrace, &errors, mapping)
            default:
                errors += [unknownKeyError(backtrace)]
        }
    }
    return result
}

```

## /Sources/AppBundle/config/keysMap.swift

```swift path="/Sources/AppBundle/config/keysMap.swift" 
import AppKit
import Common
import HotKey

private let minus = "minus"
private let equal = "equal"

private let q = "q"
private let w = "w"
private let e = "e"
private let r = "r"
private let t = "t"
private let y = "y"
private let u = "u"
private let i = "i"
private let o = "o"
private let p = "p"
private let leftSquareBracket = "leftSquareBracket"
private let rightSquareBracket = "rightSquareBracket"
private let backslash = "backslash"
private let sectionSign = "sectionSign"

private let a = "a"
private let s = "s"
private let d = "d"
private let f = "f"
private let g = "g"
private let h = "h"
private let j = "j"
private let k = "k"
private let l = "l"
private let semicolon = "semicolon"
private let quote = "quote"

private let z = "z"
private let x = "x"
private let c = "c"
private let v = "v"
private let b = "b"
private let n = "n"
private let m = "m"
private let comma = "comma"
private let period = "period"
private let slash = "slash"

func getKeysPreset(_ layout: KeyMapping.Preset) -> [String: Key] {
    return switch layout {
        case .qwerty: keyNotationToKeyCode
        case .dvorak: dvorakMap
        case .colemak: colemakMap
    }
}

extension Key: @unchecked @retroactive Sendable {}

let keyNotationToKeyCode: [String: Key] = [
    sectionSign: .section,
    "0": .zero,
    "1": .one,
    "2": .two,
    "3": .three,
    "4": .four,
    "5": .five,
    "6": .six,
    "7": .seven,
    "8": .eight,
    "9": .nine,
    minus: .minus,
    equal: .equal,

    q: .q,
    w: .w,
    e: .e,
    r: .r,
    t: .t,
    y: .y,
    u: .u,
    i: .i,
    o: .o,
    p: .p,
    leftSquareBracket: .leftBracket,
    rightSquareBracket: .rightBracket,
    backslash: .backslash,

    a: .a,
    s: .s,
    d: .d,
    f: .f,
    g: .g,
    h: .h,
    j: .j,
    k: .k,
    l: .l,
    semicolon: .semicolon,
    quote: .quote,

    z: .z,
    x: .x,
    c: .c,
    v: .v,
    b: .b,
    n: .n,
    m: .m,
    comma: .comma,
    period: .period,
    slash: .slash,

    "keypad0": .keypad0,
    "keypad1": .keypad1,
    "keypad2": .keypad2,
    "keypad3": .keypad3,
    "keypad4": .keypad4,
    "keypad5": .keypad5,
    "keypad6": .keypad6,
    "keypad7": .keypad7,
    "keypad8": .keypad8,
    "keypad9": .keypad9,
    "keypadClear": .keypadClear,
    "keypadDecimalMark": .keypadDecimal,
    "keypadDivide": .keypadDivide,
    "keypadEnter": .keypadEnter,
    "keypadEqual": .keypadEquals,
    "keypadMinus": .keypadMinus,
    "keypadMultiply": .keypadMultiply,
    "keypadPlus": .keypadPlus,

    "pageUp": .pageUp,
    "pageDown": .pageDown,
    "home": .home,
    "end": .end,
    "forwardDelete": .forwardDelete,

    "f1": .f1,
    "f2": .f2,
    "f3": .f3,
    "f4": .f4,
    "f5": .f5,
    "f6": .f6,
    "f7": .f7,
    "f8": .f8,
    "f9": .f9,
    "f10": .f10,
    "f11": .f11,
    "f12": .f12,
    "f13": .f13,
    "f14": .f14,
    "f15": .f15,
    "f16": .f16,
    "f17": .f17,
    "f18": .f18,
    "f19": .f19,
    "f20": .f20,

    "backtick": .grave,
    "space": .space,
    "enter": .return,
    "esc": .escape,
    "backspace": .delete,
    "tab": .tab,

    "left": .leftArrow,
    "down": .downArrow,
    "up": .upArrow,
    "right": .rightArrow,
]

private let dvorakMap: [String: Key] = keyNotationToKeyCode + [
    leftSquareBracket: .minus,
    rightSquareBracket: .equal,

    quote: .q,
    comma: .w,
    period: .e,
    p: .r,
    y: .t,
    f: .y,
    g: .u,
    c: .i,
    r: .o,
    l: .p,
    slash: .leftBracket, // leftBracket -> leftSquareBracket
    equal: .rightBracket, // rightBracket -> rightSquareBracket
    backslash: .backslash,

    a: .a,
    o: .s,
    e: .d,
    u: .f,
    i: .g,
    d: .h,
    h: .j,
    t: .k,
    n: .l,
    s: .semicolon,
    minus: .quote,

    semicolon: .z,
    q: .x,
    j: .c,
    k: .v,
    x: .b,
    b: .n,
    m: .m,
    w: .comma,
    v: .period,
    z: .slash,
]

private let colemakMap: [String: Key] = keyNotationToKeyCode + [
    q: .q,
    w: .w,
    f: .e,
    p: .r,
    g: .t,
    j: .y,
    l: .u,
    u: .i,
    y: .o,
    semicolon: .p,
    leftSquareBracket: .leftBracket,
    rightSquareBracket: .rightBracket,
    backslash: .backslash,

    a: .a,
    r: .s,
    s: .d,
    t: .f,
    d: .g,
    h: .h,
    n: .j,
    e: .k,
    i: .l,
    o: .semicolon,
    quote: .quote,

    z: .z,
    x: .x,
    c: .c,
    v: .v,
    b: .b,
    k: .n,
    m: .m,
    comma: .comma,
    period: .period,
    slash: .slash,
]

let modifiersMap: [String: NSEvent.ModifierFlags] = [
    "shift": .shift,
    "alt": .option,
    "ctrl": .control,
    "cmd": .command,
]

extension NSEvent.ModifierFlags {
    func toString() -> String {
        var result: [String] = []
        if contains(.option) { result.append("alt") }
        if contains(.control) { result.append("ctrl") }
        if contains(.command) { result.append("cmd") }
        if contains(.shift) { result.append("shift") }
        return result.joined(separator: "-")
    }
}

extension Key {
    func toString() -> String {
        switch self {
            case .a: "a"
            case .b: "b"
            case .c: "c"
            case .d: "d"
            case .e: "e"
            case .f: "f"
            case .g: "g"
            case .h: "h"
            case .i: "i"
            case .j: "j"
            case .k: "k"
            case .l: "l"
            case .m: "m"
            case .n: "n"
            case .o: "o"
            case .p: "p"
            case .q: "q"
            case .r: "r"
            case .s: "s"
            case .t: "t"
            case .u: "u"
            case .v: "v"
            case .w: "w"
            case .x: "x"
            case .y: "y"
            case .z: "z"

            case .zero: "0"
            case .one: "1"
            case .two: "2"
            case .three: "3"
            case .four: "4"
            case .five: "5"
            case .six: "6"
            case .seven: "7"
            case .eight: "8"
            case .nine: "9"

            case .period: "period"
            case .quote: "quote"
            case .leftBracket: "leftSquareBracket"
            case .rightBracket: "rightSquareBracket"
            case .semicolon: "semicolon"
            case .slash: "slash"
            case .backslash: "backslash"
            case .comma: "comma"
            case .equal: "equal"
            case .grave: "backtick"
            case .minus: "minus"
            case .space: "space"
            case .tab: "tab"
            case .return: "enter"
            case .pageUp: "pageUp"
            case .pageDown: "pageDown"
            case .home: "home"
            case .end: "end"
            case .leftArrow: "left"
            case .downArrow: "down"
            case .upArrow: "up"
            case .rightArrow: "right"
            case .escape: "esc"
            case .delete: "backspace"
            case .section: "sectionSign"

            case .f1: "f1"
            case .f2: "f2"
            case .f3: "f3"
            case .f4: "f4"
            case .f5: "f5"
            case .f6: "f6"
            case .f7: "f7"
            case .f8: "f8"
            case .f9: "f9"
            case .f10: "f10"
            case .f11: "f11"
            case .f12: "f12"
            case .f13: "f13"
            case .f14: "f14"
            case .f15: "f15"
            case .f16: "f16"
            case .f17: "f17"
            case .f18: "f18"
            case .f19: "f19"
            case .f20: "f20"

            case .keypad0: "keypad0"
            case .keypad1: "keypad1"
            case .keypad2: "keypad2"
            case .keypad3: "keypad3"
            case .keypad4: "keypad4"
            case .keypad5: "keypad5"
            case .keypad6: "keypad6"
            case .keypad7: "keypad7"
            case .keypad8: "keypad8"
            case .keypad9: "keypad9"
            case .keypadClear: "keypadClear"
            case .keypadDecimal: "keypadDecimalMark"
            case .keypadDivide: "keypadDivide"
            case .keypadEnter: "keypadEnter"
            case .keypadEquals: "keypadEqual"
            case .keypadMinus: "keypadMinus"
            case .keypadMultiply: "keypadMultiply"
            case .keypadPlus: "keypadPlus"

            // wtf
            case .command: "cmd"
            case .rightCommand: "rCmd"
            case .option: "alt"
            case .rightOption: "rAlt"
            case .control: "ctrl"
            case .rightControl: "rCtrl"
            case .shift: "shift"
            case .rightShift: "rShift"
            case .function: "function"
            case .capsLock: "capsLock"
            case .forwardDelete: "forwardDelete"
            case .help: "help"
            case .volumeUp: "volumeUp"
            case .volumeDown: "volumeDown"
            case .mute: "mute"
        }
    }
}

// doesn't work :(
//extension NSEvent.ModifierFlags {
//    static let lOption = NSEvent.ModifierFlags(rawValue: 1 << 1)
//    static let rOption = NSEvent.ModifierFlags(rawValue: 1 << 2)
//    static let lShift = NSEvent.ModifierFlags(rawValue: 0x00000002)
//    static let rShift = NSEvent.ModifierFlags(rawValue: 0x00000004)
//    static let lCommand = NSEvent.ModifierFlags(rawValue: 1 << 7)
//    static let rCommand = NSEvent.ModifierFlags(rawValue: 0x00000010)
//}

// NSEvent.ModifierFlags.command.rawValue // 1 << 20
// NSEvent.ModifierFlags.option.rawValue // 1 << 19
// NSEvent.ModifierFlags.control.rawValue // 1 << 18
// NSEvent.ModifierFlags.shift.rawValue // 1 << 17
// https://github.com/koekeishiya/skhd/blob/master/src/hotkey.h

```

## /Sources/AppBundle/config/parseConfig.swift

```swift path="/Sources/AppBundle/config/parseConfig.swift" 
import AppKit
import Common
import HotKey
import TOMLKit

@MainActor
func readConfig(forceConfigUrl: URL? = nil) -> Result<(Config, URL), String> {
    let customConfigUrl: URL
    switch findCustomConfigUrl() {
        case .file(let url): customConfigUrl = url
        case .noCustomConfigExists: customConfigUrl = defaultConfigUrl
        case .ambiguousConfigError(let candidates):
            let msg = """
                Ambiguous config error. Several configs found:
                \(candidates.map(\.path).joined(separator: "\n"))
                """
            return .failure(msg)
    }
    let configUrl: URL = forceConfigUrl ?? customConfigUrl
    let (parsedConfig, errors) = (try? String(contentsOf: configUrl)).map { parseConfig($0) } ?? (defaultConfig, [])

    if errors.isEmpty {
        return .success((parsedConfig, configUrl))
    } else {
        let msg = """
            Failed to parse \(configUrl.absoluteURL.path)

            \(errors.map(\.description).joined(separator: "\n\n"))
            """
        return .failure(msg)
    }
}

enum TomlParseError: Error, CustomStringConvertible, Equatable {
    case semantic(_ backtrace: TomlBacktrace, _ message: String)
    case syntax(_ message: String)

    var description: String {
        return switch self {
            // todo Make 'split' + flatten normalization prettier
            case .semantic(let backtrace, let message): backtrace.isEmptyRoot ? message : "\(backtrace): \(message)"
            case .syntax(let message): message
        }
    }
}

typealias ParsedToml<T> = Result<T, TomlParseError>

extension ParserProtocol {
    func transformRawConfig(_ raw: S,
                            _ value: TOMLValueConvertible,
                            _ backtrace: TomlBacktrace,
                            _ errors: inout [TomlParseError]) -> S
    {
        if let value = parse(value, backtrace, &errors).getOrNil(appendErrorTo: &errors) {
            return raw.copy(keyPath, value)
        }
        return raw
    }
}

protocol ParserProtocol<S>: Sendable {
    associatedtype T
    associatedtype S where S: ConvenienceCopyable
    var keyPath: SendableWritableKeyPath<S, T> { get }
    var parse: @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> ParsedToml<T> { get }
}

struct Parser<S: ConvenienceCopyable, T>: ParserProtocol {
    let keyPath: SendableWritableKeyPath<S, T>
    let parse: @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> ParsedToml<T>

    init(_ keyPath: SendableWritableKeyPath<S, T>, _ parse: @escaping @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> T) {
        self.keyPath = keyPath
        self.parse = { raw, backtrace, errors -> ParsedToml<T> in .success(parse(raw, backtrace, &errors)) }
    }

    init(_ keyPath: SendableWritableKeyPath<S, T>, _ parse: @escaping @Sendable (TOMLValueConvertible, TomlBacktrace) -> ParsedToml<T>) {
        self.keyPath = keyPath
        self.parse = { raw, backtrace, _ -> ParsedToml<T> in parse(raw, backtrace) }
    }
}

private let keyMappingConfigRootKey = "key-mapping"
private let modeConfigRootKey = "mode"

// For every new config option you add, think:
// 1. Does it make sense to have different value
// 2. Prefer commands and commands flags over toml options if possible
private let configParser: [String: any ParserProtocol<Config>] = [
    "after-login-command": Parser(\.afterLoginCommand, parseAfterLoginCommand),
    "after-startup-command": Parser(\.afterStartupCommand) { parseCommandOrCommands($0).toParsedToml($1) },

    "on-focus-changed": Parser(\.onFocusChanged) { parseCommandOrCommands($0).toParsedToml($1) },
    "on-focused-monitor-changed": Parser(\.onFocusedMonitorChanged) { parseCommandOrCommands($0).toParsedToml($1) },
    // "on-focused-workspace-changed": Parser(\.onFocusedWorkspaceChanged, { parseCommandOrCommands($0).toParsedToml($1) }),

    "enable-normalization-flatten-containers": Parser(\.enableNormalizationFlattenContainers, parseBool),
    "enable-normalization-opposite-orientation-for-nested-containers": Parser(\.enableNormalizationOppositeOrientationForNestedContainers, parseBool),

    "default-root-container-layout": Parser(\.defaultRootContainerLayout, parseLayout),
    "default-root-container-orientation": Parser(\.defaultRootContainerOrientation, parseDefaultContainerOrientation),

    "start-at-login": Parser(\.startAtLogin, parseBool),
    "automatically-unhide-macos-hidden-apps": Parser(\.automaticallyUnhideMacosHiddenApps, parseBool),
    "accordion-padding": Parser(\.accordionPadding, parseInt),
    "exec-on-workspace-change": Parser(\.execOnWorkspaceChange, parseExecOnWorkspaceChange),
    "exec": Parser(\.execConfig, parseExecConfig),

    keyMappingConfigRootKey: Parser(\.keyMapping, skipParsing(Config().keyMapping)), // Parsed manually
    modeConfigRootKey: Parser(\.modes, skipParsing(Config().modes)), // Parsed manually

    "gaps": Parser(\.gaps, parseGaps),
    "workspace-to-monitor-force-assignment": Parser(\.workspaceToMonitorForceAssignment, parseWorkspaceToMonitorAssignment),
    "on-window-detected": Parser(\.onWindowDetected, parseOnWindowDetectedArray),

    // Deprecated
    "non-empty-workspaces-root-containers-layout-on-startup": Parser(\._nonEmptyWorkspacesRootContainersLayoutOnStartup, parseStartupRootContainerLayout),
    "indent-for-nested-containers-with-the-same-orientation": Parser(\._indentForNestedContainersWithTheSameOrientation, parseIndentForNestedContainersWithTheSameOrientation),
]

extension ParsedCmd where T == any Command {
    fileprivate func toEither() -> Parsed<T> {
        return switch self {
            case .cmd(let a):
                a.info.allowInConfig
                    ? .success(a)
                    : .failure("Command '\(a.info.kind.rawValue)' cannot be used in config")
            case .help(let a): .failure(a)
            case .failure(let a): .failure(a)
        }
    }
}

extension Command {
    fileprivate var isMacOsNativeCommand: Bool { // Problem ID-B6E178F2
        self is MacosNativeMinimizeCommand || self is MacosNativeFullscreenCommand
    }
}

func parseAfterLoginCommand(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<[any Command]> {
    if let array = raw.array, array.count == 0 {
        return .success([])
    }
    let msg = "after-login-command is deprecated since AeroSpace 0.19.0. https://github.com/nikitabobko/AeroSpace/issues/1482"
    return .failure(.semantic(backtrace, msg))
}

func parseCommandOrCommands(_ raw: TOMLValueConvertible) -> Parsed<[any Command]> {
    if let rawString = raw.string {
        return parseCommand(rawString).toEither().map { [$0] }
    } else if let rawArray = raw.array {
        let commands: Parsed<[any Command]> = (0 ..< rawArray.count).mapAllOrFailure { index in
            let rawString: String = rawArray[index].string ?? expectedActualTypeError(expected: .string, actual: rawArray[index].type)
            return parseCommand(rawString).toEither()
        }
        return commands.filter("macos-native-* commands are only allowed to be the last commands in the list") {
            !$0.dropLast().contains(where: { $0.isMacOsNativeCommand })
        }
    } else {
        return .failure(expectedActualTypeError(expected: [.string, .array], actual: raw.type))
    }
}

@MainActor func parseConfig(_ rawToml: String) -> (config: Config, errors: [TomlParseError]) { // todo change return value to Result
    let rawTable: TOMLTable
    do {
        rawTable = try TOMLTable(string: rawToml)
    } catch let e as TOMLParseError {
        return (defaultConfig, [.syntax(e.debugDescription)])
    } catch let e {
        return (defaultConfig, [.syntax(e.localizedDescription)])
    }

    var errors: [TomlParseError] = []

    var config = rawTable.parseTable(Config(), configParser, .emptyRoot, &errors)

    if let mapping = rawTable[keyMappingConfigRootKey].flatMap({ parseKeyMapping($0, .rootKey(keyMappingConfigRootKey), &errors) }) {
        config.keyMapping = mapping
    }

    if let modes = rawTable[modeConfigRootKey].flatMap({ parseModes($0, .rootKey(modeConfigRootKey), &errors, config.keyMapping.resolve()) }) {
        config.modes = modes
    }

    config.preservedWorkspaceNames = config.modes.values.lazy
        .flatMap { (mode: Mode) -> [HotkeyBinding] in Array(mode.bindings.values) }
        .flatMap { (binding: HotkeyBinding) -> [String] in
            binding.commands.filterIsInstance(of: WorkspaceCommand.self).compactMap { $0.args.target.val.workspaceNameOrNil()?.raw } +
                binding.commands.filterIsInstance(of: MoveNodeToWorkspaceCommand.self).compactMap { $0.args.target.val.workspaceNameOrNil()?.raw }
        }
        + (config.workspaceToMonitorForceAssignment).keys

    if config.enableNormalizationFlattenContainers {
        let containsSplitCommand = config.modes.values.lazy.flatMap { $0.bindings.values }
            .flatMap { $0.commands }
            .contains { $0 is SplitCommand }
        if containsSplitCommand {
            errors += [.semantic(
                .emptyRoot, // todo Make 'split' + flatten normalization prettier
                """
                The config contains:
                1. usage of 'split' command
                2. enable-normalization-flatten-containers = true
                These two settings don't play nicely together. 'split' command has no effect when enable-normalization-flatten-containers is disabled.

                My recommendation: keep the normalizations enabled, and prefer 'join-with' over 'split'.
                """,
            )]
        }
    }
    return (config, errors)
}

func parseIndentForNestedContainersWithTheSameOrientation(
    _ raw: TOMLValueConvertible,
    _ backtrace: TomlBacktrace,
) -> ParsedToml<Void> {
    let msg = "Deprecated. Please drop it from the config. See https://github.com/nikitabobko/AeroSpace/issues/96"
    return .failure(.semantic(backtrace, msg))
}

func parseInt(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<Int> {
    raw.int.orFailure(expectedActualTypeError(expected: .int, actual: raw.type, backtrace))
}

func parseString(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<String> {
    raw.string.orFailure(expectedActualTypeError(expected: .string, actual: raw.type, backtrace))
}

func parseSimpleType<T>(_ raw: TOMLValueConvertible) -> T? {
    (raw.int as? T) ?? (raw.string as? T) ?? (raw.bool as? T)
}

extension TOMLValueConvertible {
    func unwrapTableWithSingleKey(expectedKey: String? = nil, _ backtrace: inout TomlBacktrace) -> ParsedToml<(key: String, value: TOMLValueConvertible)> {
        guard let table else {
            return .failure(expectedActualTypeError(expected: .table, actual: type, backtrace))
        }
        let singleKeyError: TomlParseError = .semantic(
            backtrace,
            expectedKey != nil
                ? "The table is expected to have a single key '\(expectedKey.orDie())'"
                : "The table is expected to have a single key",
        )
        guard let (actualKey, value): (String, TOMLValueConvertible) = table.count == 1 ? table.first : nil else {
            return .failure(singleKeyError)
        }
        if expectedKey != nil && expectedKey != actualKey {
            return .failure(singleKeyError)
        }
        backtrace = backtrace + .key(actualKey)
        return .success((actualKey, value))
    }
}

func parseTomlArray(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<TOMLArray> {
    raw.array.orFailure(expectedActualTypeError(expected: .array, actual: raw.type, backtrace))
}

func parseTable<T: ConvenienceCopyable>(
    _ raw: TOMLValueConvertible,
    _ initial: T,
    _ fieldsParser: [String: any ParserProtocol<T>],
    _ backtrace: TomlBacktrace,
    _ errors: inout [TomlParseError],
) -> T {
    guard let table = raw.table else {
        errors.append(expectedActualTypeError(expected: .table, actual: raw.type, backtrace))
        return initial
    }
    return table.parseTable(initial, fieldsParser, backtrace, &errors)
}

private func parseStartupRootContainerLayout(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<Void> {
    parseString(raw, backtrace)
        .filter(.semantic(backtrace, "'non-empty-workspaces-root-containers-layout-on-startup' is deprecated. Please drop it from your config")) { raw in raw == "smart" }
        .map { _ in () }
}

private func parseLayout(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<Layout> {
    parseString(raw, backtrace)
        .flatMap { $0.parseLayout().orFailure(.semantic(backtrace, "Can't parse layout '\($0)'")) }
}

private func skipParsing<T: Sendable>(_ value: T) -> @Sendable (_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<T> {
    { _, _ in .success(value) }
}

private func parseExecOnWorkspaceChange(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<[String]> {
    parseTomlArray(raw, backtrace)
        .flatMap { arr in
            arr.mapAllOrFailure { elem in parseString(elem, backtrace) }
        }
}

private func parseDefaultContainerOrientation(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<DefaultContainerOrientation> {
    parseString(raw, backtrace).flatMap {
        DefaultContainerOrientation(rawValue: $0)
            .orFailure(.semantic(backtrace, "Can't parse default container orientation '\($0)'"))
    }
}

extension Parsed where Failure == String {
    func toParsedToml(_ backtrace: TomlBacktrace) -> ParsedToml<Success> {
        mapError { .semantic(backtrace, $0) }
    }
}

func parseBool(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<Bool> {
    raw.bool.orFailure(expectedActualTypeError(expected: .bool, actual: raw.type, backtrace))
}

indirect enum TomlBacktrace: CustomStringConvertible, Equatable {
    case emptyRoot
    case rootKey(String)
    case key(String)
    case index(Int)
    case pair(TomlBacktrace, TomlBacktrace)

    var description: String {
        return switch self {
            case .emptyRoot: dieT("Impossible")
            case .rootKey(let value): value
            case .key(let value): "." + value
            case .index(let index): "[\(index)]"
            case .pair(let first, let second): first.description + second.description
        }
    }

    var isEmptyRoot: Bool {
        return switch self {
            case .emptyRoot: true
            default: false
        }
    }

    var isRootKey: Bool {
        return switch self {
            case .rootKey: true
            default: false
        }
    }

    static func + (lhs: TomlBacktrace, rhs: TomlBacktrace) -> TomlBacktrace {
        if case .emptyRoot = lhs {
            if case .key(let newRoot) = rhs {
                return .rootKey(newRoot)
            } else {
                die("Impossible")
            }
        } else {
            return pair(lhs, rhs)
        }
    }
}

extension TOMLTable {
    func parseTable<T: ConvenienceCopyable>(
        _ initial: T,
        _ fieldsParser: [String: any ParserProtocol<T>],
        _ backtrace: TomlBacktrace,
        _ errors: inout [TomlParseError],
    ) -> T {
        var raw = initial

        for (key, value) in self {
            let backtrace: TomlBacktrace = backtrace + .key(key)
            if let parser = fieldsParser[key] {
                raw = parser.transformRawConfig(raw, value, backtrace, &errors)
            } else {
                errors.append(unknownKeyError(backtrace))
            }
        }

        return raw
    }
}

func unknownKeyError(_ backtrace: TomlBacktrace) -> TomlParseError {
    .semantic(backtrace, backtrace.isRootKey ? "Unknown top-level key" : "Unknown key")
}

func expectedActualTypeError(expected: TOMLType, actual: TOMLType, _ backtrace: TomlBacktrace) -> TomlParseError {
    .semantic(backtrace, expectedActualTypeError(expected: expected, actual: actual))
}

func expectedActualTypeError(expected: [TOMLType], actual: TOMLType, _ backtrace: TomlBacktrace) -> TomlParseError {
    .semantic(backtrace, expectedActualTypeError(expected: expected, actual: actual))
}

```

## /Sources/AppBundle/config/parseExecEnvVariables.swift

```swift path="/Sources/AppBundle/config/parseExecEnvVariables.swift" 
import AppKit
import Common
import TOMLKit

let testEnv = ["PATH": "AEROSPACE_TEST_PATH", "AEROSPACE_INHERITED_TEST_ENV": "inherited"]
private var env: [String: String] {
    isUnitTest ? testEnv : ProcessInfo.processInfo.environment
}

private let rawExecConfigParser: [String: any ParserProtocol<RawExecConfig>] = [
    "inherit-env-vars": Parser(\.inheritEnvVariables, parseBool),
    "env-vars": Parser(\.overriddenVars, parseEnvVariables),
]

let defaultOverriddenEnvVars = ["PATH": "/opt/homebrew/bin:/opt/homebrew/sbin:\(env["PATH"] ?? "")"]

struct ExecConfig: Equatable {
    var envVariables: [String: String] = env + defaultOverriddenEnvVars
}

struct RawExecConfig: ConvenienceCopyable, Equatable {
    var inheritEnvVariables = true
    // Already interpolated value of overridden vars
    var overriddenVars: [String: String] = [:]

    func expand() -> ExecConfig {
        let base: [String: String] = inheritEnvVariables ? env : [:]
        return ExecConfig(envVariables: base + overriddenVars)
    }
}

func parseExecConfig(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> ExecConfig {
    parseTable(raw, RawExecConfig(), rawExecConfigParser, backtrace, &errors).expand()
}

private func parseEnvVariables(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [String: String] {
    guard let table = raw.table else {
        errors.append(expectedActualTypeError(expected: .array, actual: raw.type, backtrace))
        return [:]
    }
    let mutated = table.keys
    let fullEnv: [String: String] = env
    let baseEnv: [String: String] = fullEnv.filter { (key, _) -> Bool in !mutated.contains(key) }
    var result: [String: String] = [:]
    for (key, value) in table {
        let backtrace = backtrace + .key(key)
        if key == "PWD" { errors.append(.semantic(backtrace, "Changing 'PWD' is not allowed")) }
        guard let rawStr = parseString(value, backtrace).getOrNil(appendErrorTo: &errors) else { continue }
        var env = baseEnv
        if let add: String = fullEnv[key] {
            env[key] = add
        }
        switch rawStr.interpolate(with: env) {
            case .success(let interpolated): result[key] = interpolated
            case .failure(let _errros): errors += _errros.map { .semantic(backtrace, $0) }
        }
    }
    return result
}

```

## /Sources/AppBundle/config/startAtLogin.swift

```swift path="/Sources/AppBundle/config/startAtLogin.swift" 
import AppKit
import Common
import ServiceManagement

@MainActor
func syncStartAtLogin() {
    cleanupPlistFromPrevVersions()
    let service = SMAppService.mainApp
    if config.startAtLogin {
        if isDebug {
            print("'start-at-login = true' has no effect in debug builds")
        } else {
            _ = try? service.register()
        }
    } else {
        _ = try? service.unregister()
    }
}

private func cleanupPlistFromPrevVersions() { // todo Drop after a couple of versions
    let launchAgentsDir = FileManager.default.homeDirectoryForCurrentUser.appending(component: "Library/LaunchAgents/")
    Result { try FileManager.default.createDirectory(at: launchAgentsDir, withIntermediateDirectories: true) }.getOrDie()
    let url: URL = launchAgentsDir.appending(path: "bobko.aerospace.plist")
    try? FileManager.default.removeItem(at: url)
}

```

## /Sources/AppBundle/util/NSRunningApplicationEx.swift

```swift path="/Sources/AppBundle/util/NSRunningApplicationEx.swift" 
import AppKit

extension NSRunningApplication {
    var idForDebug: String {
        "PID: \(processIdentifier) ID: \(bundleIdentifier ?? executableURL?.description ?? "")"
    }
}

```


The content has been capped at 50000 tokens. The user could consider applying other filters to refine the result. The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.
Copied!