Repository: nikitabobko/AeroSpace Branch: main Commit: 85f80bda2d7b Files: 487 Total size: 1.4 MB Directory structure: gitextract_klfaytgz/ ├── .bundle/ │ └── config ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── DISCUSSION_TEMPLATE/ │ │ └── potential-bugs.yml │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── config.yml │ │ └── new-issue.yml │ ├── gh-actions-runner-xcode-select.sh │ ├── pull_request_template.md │ └── workflows/ │ ├── build.yml │ └── close-third-party-issues.yml ├── .gitignore ├── .swift-version ├── .swiftformat ├── .swiftlint.yml ├── AeroSpace.xcodeproj/ │ ├── project.pbxproj │ └── project.xcworkspace/ │ └── contents.xcworkspacedata ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── Package.resolved ├── Package.swift ├── README.md ├── ShellParserGenerated/ │ ├── .gitignore │ ├── Package.swift │ └── Sources/ │ └── ShellParserGenerated/ │ ├── ShellLexer.swift │ └── ShellParser.swift ├── Sources/ │ ├── AeroSpaceApp/ │ │ └── AeroSpaceApp.swift │ ├── AppBundle/ │ │ ├── GlobalObserver.swift │ │ ├── command/ │ │ │ ├── CmdEnv.swift │ │ │ ├── CmdIo.swift │ │ │ ├── Command.swift │ │ │ ├── cmdManifest.swift │ │ │ ├── cmdResolveTargetOrReportError.swift │ │ │ ├── format.swift │ │ │ ├── formatToJson.swift │ │ │ ├── impl/ │ │ │ │ ├── BalanceSizesCommand.swift │ │ │ │ ├── CloseAllWindowsButCurrentCommand.swift │ │ │ │ ├── CloseCommand.swift │ │ │ │ ├── ConfigCommand.swift │ │ │ │ ├── DebugWindowsCommand.swift │ │ │ │ ├── EnableCommand.swift │ │ │ │ ├── ExecAndForgetCommand.swift │ │ │ │ ├── FlattenWorkspaceTreeCommand.swift │ │ │ │ ├── FocusBackAndForthCommand.swift │ │ │ │ ├── FocusCommand.swift │ │ │ │ ├── FocusMonitorCommand.swift │ │ │ │ ├── FullscreenCommand.swift │ │ │ │ ├── JoinWithCommand.swift │ │ │ │ ├── LayoutCommand.swift │ │ │ │ ├── ListAppsCommand.swift │ │ │ │ ├── ListExecEnvVarsCommand.swift │ │ │ │ ├── ListModesCommand.swift │ │ │ │ ├── ListMonitorsCommand.swift │ │ │ │ ├── ListWindowsCommand.swift │ │ │ │ ├── ListWorkspacesCommand.swift │ │ │ │ ├── MacosNativeFullscreenCommand.swift │ │ │ │ ├── MacosNativeMinimizeCommand.swift │ │ │ │ ├── ModeCommand.swift │ │ │ │ ├── MoveCommand.swift │ │ │ │ ├── MoveMouseCommand.swift │ │ │ │ ├── MoveNodeToMonitorCommand.swift │ │ │ │ ├── MoveNodeToWorkspaceCommand.swift │ │ │ │ ├── MoveWorkspaceToMonitorCommand.swift │ │ │ │ ├── ReloadConfigCommand.swift │ │ │ │ ├── ResizeCommand.swift │ │ │ │ ├── SplitCommand.swift │ │ │ │ ├── SummonWorkspaceCommand.swift │ │ │ │ ├── SwapCommand.swift │ │ │ │ ├── TriggerBindingCommand.swift │ │ │ │ ├── VolumeCommand.swift │ │ │ │ ├── WorkspaceBackAndForthCommand.swift │ │ │ │ └── WorkspaceCommand.swift │ │ │ └── parseCommand.swift │ │ ├── config/ │ │ │ ├── Config.swift │ │ │ ├── ConfigFile.swift │ │ │ ├── ConfigFileWatcher.swift │ │ │ ├── DynamicConfigValue.swift │ │ │ ├── HotkeyBinding.swift │ │ │ ├── Mode.swift │ │ │ ├── keysMap.swift │ │ │ ├── parseConfig.swift │ │ │ ├── parseExecEnvVariables.swift │ │ │ ├── parseGaps.swift │ │ │ ├── parseKeyMapping.swift │ │ │ ├── parseOnWindowDetected.swift │ │ │ ├── parseWorkspaceToMonitorAssignment.swift │ │ │ └── startAtLogin.swift │ │ ├── focus.swift │ │ ├── focusCache.swift │ │ ├── getNativeFocusedWindow.swift │ │ ├── initAppBundle.swift │ │ ├── layout/ │ │ │ ├── layoutRecursive.swift │ │ │ └── refresh.swift │ │ ├── model/ │ │ │ ├── AxUiElementWindowType.swift │ │ │ ├── Json.swift │ │ │ ├── KnownBundleId.swift │ │ │ ├── Monitor.swift │ │ │ ├── MonitorDescriptionEx.swift │ │ │ ├── MonitorEx.swift │ │ │ ├── Rect.swift │ │ │ └── ServerEvent.swift │ │ ├── mouse/ │ │ │ ├── mouse.swift │ │ │ ├── moveWithMouse.swift │ │ │ └── resizeWithMouse.swift │ │ ├── normalizeLayoutReason.swift │ │ ├── runLoop.swift │ │ ├── server.swift │ │ ├── shell/ │ │ │ └── Shell.swift │ │ ├── subscriptions.swift │ │ ├── tree/ │ │ │ ├── AbstractApp.swift │ │ │ ├── MacApp.swift │ │ │ ├── MacWindow.swift │ │ │ ├── MacosUnconventionalWindowsContainer.swift │ │ │ ├── TilingContainer.swift │ │ │ ├── TreeNode.swift │ │ │ ├── TreeNodeCases.swift │ │ │ ├── TreeNodeEx.swift │ │ │ ├── Window.swift │ │ │ ├── Workspace.swift │ │ │ ├── WorkspaceEx.swift │ │ │ ├── frozen/ │ │ │ │ ├── FrozenTreeNode.swift │ │ │ │ ├── FrozenWorld.swift │ │ │ │ └── closedWindowsCache.swift │ │ │ └── normalizeContainers.swift │ │ ├── ui/ │ │ │ ├── AppearanceTheme.swift │ │ │ ├── ExperimentalUISettings.swift │ │ │ ├── MenuBar.swift │ │ │ ├── MenuBarLabel.swift │ │ │ ├── MessageView.swift │ │ │ ├── NSPanelHud.swift │ │ │ ├── SecureInputView.swift │ │ │ ├── TrayMenuModel.swift │ │ │ └── VolumeView.swift │ │ ├── util/ │ │ │ ├── ArrayEx.swift │ │ │ ├── AwaitableOneTimeBroadcastLatch.swift │ │ │ ├── AxSubscription.swift │ │ │ ├── AxUiElementMock.swift │ │ │ ├── LazySequenceProtocolEx.swift │ │ │ ├── MruStack.swift │ │ │ ├── NSRunningApplicationEx.swift │ │ │ ├── NsApplicationEx.swift │ │ │ ├── SetEx.swift │ │ │ ├── ThreadGuardedValue.swift │ │ │ ├── UniqueToken.swift │ │ │ ├── accessibility.swift │ │ │ ├── appBundleUtil.swift │ │ │ ├── axTrustedCheckOptionPrompt.swift │ │ │ └── dumpAxRecursive.swift │ │ └── windowLevelCache.swift │ ├── AppBundleTests/ │ │ ├── AxUiElementWindowTypeTest.swift │ │ ├── assert.swift │ │ ├── command/ │ │ │ ├── BalanceSizesCommandTest.swift │ │ │ ├── CloseCommandTest.swift │ │ │ ├── ExecCommandTest.swift │ │ │ ├── FlattenWorkspaceTreeCommandTest.swift │ │ │ ├── FocusCommandTest.swift │ │ │ ├── JoinWithCommandTest.swift │ │ │ ├── ListAppsTest.swift │ │ │ ├── ListModesTest.swift │ │ │ ├── ListMonitorsTest.swift │ │ │ ├── ListWindowsTest.swift │ │ │ ├── ListWorkspacesTest.swift │ │ │ ├── MoveCommandTest.swift │ │ │ ├── MoveNodeToMonitorCommandTest.swift │ │ │ ├── MoveNodeToWorkspaceCommandTest.swift │ │ │ ├── ResizeCommandTest.swift │ │ │ ├── SplitCommandTest.swift │ │ │ ├── SubscribeCmdArgsTest.swift │ │ │ ├── SummonWorkspaceCommandTest.swift │ │ │ ├── SwapCommandTest.swift │ │ │ └── WorkspaceCommandTest.swift │ │ ├── config/ │ │ │ ├── ConfigTest.swift │ │ │ ├── ParseEnvVariablesTest.swift │ │ │ └── SplitArgsTest.swift │ │ ├── model/ │ │ │ └── ClientServerTest.swift │ │ ├── shell/ │ │ │ └── ShellTest.swift │ │ ├── testExtensions.swift │ │ ├── testUtil.swift │ │ └── tree/ │ │ ├── TestApp.swift │ │ ├── TestWindow.swift │ │ ├── TilingContainer.swift │ │ └── TreeNodeTest.swift │ ├── Cli/ │ │ ├── _main.swift │ │ ├── cliUtil.swift │ │ └── subcommandDescriptionsGenerated.swift │ ├── Common/ │ │ ├── appMetadata.swift │ │ ├── cmdArgs/ │ │ │ ├── ArgParser.swift │ │ │ ├── ArgParserInput.swift │ │ │ ├── SubArgParser.swift │ │ │ ├── cmdArgsManifest.swift │ │ │ ├── impl/ │ │ │ │ ├── BalanceSizesCmdArgs.swift │ │ │ │ ├── CloseAllWindowsButCurrentCmdArgs.swift │ │ │ │ ├── CloseCmdArgs.swift │ │ │ │ ├── ConfigCmdArgs.swift │ │ │ │ ├── DebugWindowsCmdArgs.swift │ │ │ │ ├── EnableCmdArgs.swift │ │ │ │ ├── ExecAndForgetCmdArgs.swift │ │ │ │ ├── FlattenWorkspaceTreeCmdArgs.swift │ │ │ │ ├── FocusBackAndForthCmdArgs.swift │ │ │ │ ├── FocusCmdArgs.swift │ │ │ │ ├── FocusMonitorCmdArgs.swift │ │ │ │ ├── FullscreenCmdArgs.swift │ │ │ │ ├── JoinWithCmdArgs.swift │ │ │ │ ├── LayoutCmdArgs.swift │ │ │ │ ├── ListAppsCmdArgs.swift │ │ │ │ ├── ListExecEnvVarsCmdArgs.swift │ │ │ │ ├── ListModesCmdArgs.swift │ │ │ │ ├── ListMonitorsCmdArgs.swift │ │ │ │ ├── ListWindowsCmdArgs.swift │ │ │ │ ├── ListWorkspacesCmdArgs.swift │ │ │ │ ├── MacosNativeFullscreenCmdArgs.swift │ │ │ │ ├── MacosNativeMinimizeCmdArgs.swift │ │ │ │ ├── ModeCmdArgs.swift │ │ │ │ ├── MoveCmdArgs.swift │ │ │ │ ├── MoveMouseCmdArgs.swift │ │ │ │ ├── MoveNodeToMonitorCmdArgs.swift │ │ │ │ ├── MoveNodeToWorkspaceCmdArgs.swift │ │ │ │ ├── MoveWorkpsaceToMonitorCmdArgs.swift │ │ │ │ ├── ReloadConfigCmdArgs.swift │ │ │ │ ├── ResizeCmdArgs.swift │ │ │ │ ├── SplitCmdArgs.swift │ │ │ │ ├── SubscribeCmdArgs.swift │ │ │ │ ├── SummonWorkspaceCmdArgs.swift │ │ │ │ ├── SwapCmdArgs.swift │ │ │ │ ├── TriggerBindingCmdArgs.swift │ │ │ │ ├── VolumeCmdArgs.swift │ │ │ │ ├── WorkspaceBackAndForthCmdArgs.swift │ │ │ │ └── WorkspaceCmdArgs.swift │ │ │ ├── parseCmdArgs.swift │ │ │ ├── parseSpecificCmdArgs.swift │ │ │ ├── splitArgs.swift │ │ │ └── subcommandParsers.swift │ │ ├── cmdHelpGenerated.swift │ │ ├── gitHashGenerated.swift │ │ ├── model/ │ │ │ ├── AeroSpaceEnvVars.swift │ │ │ ├── AxAppThreadToken.swift │ │ │ ├── CardinalDirection.swift │ │ │ ├── CardinalOrDfsDirection.swift │ │ │ ├── DfsNextPrev.swift │ │ │ ├── Init.swift │ │ │ ├── MonitorDescription.swift │ │ │ ├── NextPrev.swift │ │ │ ├── Orientation.swift │ │ │ ├── WorkspaceName.swift │ │ │ ├── clientServer.swift │ │ │ └── sponsorshipPrompts.swift │ │ ├── util/ │ │ │ ├── AeroAny.swift │ │ │ ├── ArrSlice.swift │ │ │ ├── BoolEx.swift │ │ │ ├── CollectionEx.swift │ │ │ ├── ComparableEx.swift │ │ │ ├── ConvenienceCopyable.swift │ │ │ ├── DictionaryEx.swift │ │ │ ├── EquatableNoop.swift │ │ │ ├── JsonEncoderEx.swift │ │ │ ├── Lateinit.swift │ │ │ ├── MainActorEx.swift │ │ │ ├── NWConnectionEx.swift │ │ │ ├── Nullable.swift │ │ │ ├── OptionalEx.swift │ │ │ ├── ResultEx.swift │ │ │ ├── SequenceEx.swift │ │ │ ├── StringEx.swift │ │ │ ├── StringLogicalSegments.swift │ │ │ ├── commonUtil.swift │ │ │ └── showMessageInGui.swift │ │ └── versionGenerated.swift │ └── PrivateApi/ │ └── include/ │ ├── module.modulemap │ ├── private.h │ └── private.m ├── axDumps/ │ ├── 1password.json5 │ ├── 1password_large_type_window.json5 │ ├── 1password_mini_window.json5 │ ├── about_this_mac.json5 │ ├── alacritty_decorations_buttonless.json5 │ ├── apple_calendar.json5 │ ├── apple_calendar_settings.json5 │ ├── apple_followup_sign_in_to_a_new_device_confirmation.json5 │ ├── apple_mail.json5 │ ├── apple_mail_new_email.json5 │ ├── apple_mail_settings.json5 │ ├── archiveutility.json5 │ ├── brave.json5 │ ├── brave_pip.json5 │ ├── calculator.json5 │ ├── choose_1_5_0.json5 │ ├── chrome.json5 │ ├── chrome_choose_what_to_share_popup.json5 │ ├── chrome_extensions_popup.json5 │ ├── chrome_find_in_page.json5 │ ├── chrome_pip.json5 │ ├── chrome_sharing_is_in_progress_popup.json5 │ ├── cleanshotx_monitor_1.json5 │ ├── cleanshotx_monitor_2.json5 │ ├── drracket.json5 │ ├── finder.json5 │ ├── finder_quick_look.json5 │ ├── firefox.json5 │ ├── firefox_extensions_popup.json5 │ ├── firefox_mouse_hover_over_extensions_button.json5 │ ├── firefox_mouse_hover_over_tab.json5 │ ├── firefox_non_native_fullscreen.json5 │ ├── firefox_normal_window_when_non_native_fullscreen_in_background.json5 │ ├── firefox_pinterest_sign_in_with_google.json5 │ ├── firefox_pip.json5 │ ├── ghostty.json5 │ ├── ghostty_about.json5 │ ├── ghostty_check_for_updates_1_dialog.json5 │ ├── ghostty_check_for_updates_2_alert.json5 │ ├── ghostty_config_error.json5 │ ├── ghostty_quick_terminal.json5 │ ├── ghostty_window_decorations_false.json5 │ ├── intellij.json5 │ ├── intellij_background_tasks.json5 │ ├── intellij_context_menu.json5 │ ├── intellij_native_open_window.json5 │ ├── intellij_quick_doc_popup.json5 │ ├── intellij_rebase_dialog.json5 │ ├── iphonesimulator.json5 │ ├── iterm2.json5 │ ├── iterm2_hotkey_window.json5 │ ├── iterm2_no_title_bar.json5 │ ├── jetbrains_toolbox.json5 │ ├── karabiner_event_viewer.json5 │ ├── karabiner_settings.json5 │ ├── kitty_quick_access.json5 │ ├── macos_capslock_popup_safari.json5 │ ├── macos_capslock_popup_textedit.json5 │ ├── macos_join_network.json5 │ ├── macos_share_window_purple_pill_sublime.json5 │ ├── marta.json5 │ ├── microsoft_edge.json5 │ ├── microsoft_edge_pip.json5 │ ├── mpv_fullscreen.json5 │ ├── mpv_windowed.json5 │ ├── nomachine_session_1.json5 │ ├── nomachine_session_2.json5 │ ├── nomachine_welcome_window_1.json5 │ ├── nomachine_welcome_window_2.json5 │ ├── qutebrowser.json5 │ ├── qutebrowser_context_menu.json5 │ ├── qutebrowser_hide_decoration.json5 │ ├── raycast.json5 │ ├── raycast_settings.json5 │ ├── safari.json5 │ ├── safari_pinterest_sign_in_with_google.json5 │ ├── scenario_firefox_google_meet_share_window/ │ │ ├── 01_firefox.json5 │ │ ├── 02_firefox.json5 │ │ ├── 03_firefox.json5 │ │ ├── 04_firefox.json5 │ │ ├── 05_apple_controlcenter.json5 │ │ ├── 06_firefox.json5 │ │ ├── 07_firefox.json5 │ │ └── README.md │ ├── slack.json5 │ ├── slack_chat_in_a_separate_window.json5 │ ├── slack_huddle_share_screen_draw_on_screen_fake_window.json5 │ ├── slack_huddle_share_screen_floating_popup.json5 │ ├── slack_huddle_share_screen_target_picker.json5 │ ├── spotify.json5 │ ├── spotify_miniplayer.json5 │ ├── steam_1.json5 │ ├── steam_2.json5 │ ├── sublime_text_4.json5 │ ├── system_settings.json5 │ ├── telegram.json5 │ ├── telegram_image_viewer.json5 │ ├── terminal_app.json5 │ ├── transmission.json5 │ ├── transmission_torrent_inspector.json5 │ ├── vlc_empty.json5 │ ├── vlc_fullscreen.json5 │ ├── vlc_video_playing.json5 │ ├── vs_code.json5 │ ├── vs_code_nativeFullScreen_false.json5 │ ├── vs_codium.json5 │ ├── vs_codium_nativeFullScreen_false.json5 │ ├── xcode.json5 │ ├── xcode_build_succeeded_popup.json5 │ ├── xcode_installing_system_components.json5 │ ├── xcode_open_quickly.json5 │ ├── xcode_quick_actions.json5 │ ├── xcode_settings.json5 │ ├── xcode_welcome_window.json5 │ ├── zebar.json5 │ ├── zed.json5 │ ├── zen_browser.json5 │ └── zen_browser_pip.json5 ├── build-debug.sh ├── build-docs.sh ├── build-release.sh ├── build-shell-completion.sh ├── dev-docs/ │ ├── architecture.md │ └── development.md ├── docs/ │ ├── aerospace-balance-sizes.adoc │ ├── aerospace-close-all-windows-but-current.adoc │ ├── aerospace-close.adoc │ ├── aerospace-config.adoc │ ├── aerospace-debug-windows.adoc │ ├── aerospace-enable.adoc │ ├── aerospace-exec-and-forget.adoc │ ├── aerospace-flatten-workspace-tree.adoc │ ├── aerospace-focus-back-and-forth.adoc │ ├── aerospace-focus-monitor.adoc │ ├── aerospace-focus.adoc │ ├── aerospace-fullscreen.adoc │ ├── aerospace-join-with.adoc │ ├── aerospace-layout.adoc │ ├── aerospace-list-apps.adoc │ ├── aerospace-list-exec-env-vars.adoc │ ├── aerospace-list-modes.adoc │ ├── aerospace-list-monitors.adoc │ ├── aerospace-list-windows.adoc │ ├── aerospace-list-workspaces.adoc │ ├── aerospace-macos-native-fullscreen.adoc │ ├── aerospace-macos-native-minimize.adoc │ ├── aerospace-mode.adoc │ ├── aerospace-move-mouse.adoc │ ├── aerospace-move-node-to-monitor.adoc │ ├── aerospace-move-node-to-workspace.adoc │ ├── aerospace-move-workspace-to-monitor.adoc │ ├── aerospace-move.adoc │ ├── aerospace-reload-config.adoc │ ├── aerospace-resize.adoc │ ├── aerospace-split.adoc │ ├── aerospace-subscribe.adoc │ ├── aerospace-summon-workspace.adoc │ ├── aerospace-swap.adoc │ ├── aerospace-trigger-binding.adoc │ ├── aerospace-volume.adoc │ ├── aerospace-workspace-back-and-forth.adoc │ ├── aerospace-workspace.adoc │ ├── aerospace.adoc │ ├── commands.adoc │ ├── config-examples/ │ │ ├── default-config.toml │ │ └── i3-like-config-example.toml │ ├── goodies.adoc │ ├── guide.adoc │ ├── index.html │ └── util/ │ ├── all-monitors-option.adoc │ ├── conditional-arguments-header.adoc │ ├── conditional-examples-header.adoc │ ├── conditional-exit-code-header.adoc │ ├── conditional-options-header.adoc │ ├── conditional-output-format-header.adoc │ ├── header.adoc │ ├── man-attributes.adoc │ ├── man-footer.adoc │ ├── monitor-option.adoc │ ├── site-attributes.adoc │ ├── window-id-flag-desc.adoc │ └── workspace-flag-desc.adoc ├── format.sh ├── generate-shell-parser.sh ├── generate.sh ├── grammar/ │ ├── ShellLexer.g4 │ ├── ShellParser.g4 │ └── commands-bnf-grammar.txt ├── install-from-sources.sh ├── legal/ │ ├── README.md │ └── third-party-license/ │ ├── LICENSE-HotKey.txt │ ├── LICENSE-ISSoundAdditions.txt │ ├── LICENSE-TOMLKIT.txt │ ├── LICENSE-antlr.txt │ ├── LICENSE-swift-collections.txt │ └── LICENSE-tomlplusplus.txt ├── lint.sh ├── makefile ├── project.yml ├── resources/ │ ├── AeroSpace.entitlements │ └── Assets.xcassets/ │ ├── AccentColor.colorset/ │ │ └── Contents.json │ ├── AppIcon.appiconset/ │ │ └── Contents.json │ └── Contents.json ├── run-cli.sh ├── run-debug.sh ├── run-swift-test.sh ├── run-tests.sh └── script/ ├── build-brew-cask.sh ├── check-uncommitted-files.sh ├── clean-project.sh ├── clean-xcode.sh ├── generate-cmd-help.sh ├── install-dep.sh ├── publish-release.sh ├── reset-accessibility-permission-for-debug.sh └── setup.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bundle/config ================================================ --- BUNDLE_PATH: ".deps/bundler-path" BUNDLE_DISABLE_SHARED_GEMS: "1" ================================================ FILE: .editorconfig ================================================ root = true # It's better to use "wrap line" in plain text documents [*.{adoc,md}] rulers = 1000 max_line_length = 1000 [*.toml] rulers = 96 max_line_length = 96 [*.{swift,sh}] rulers = 120 max_line_length = 120 indent_style = space indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [COMMIT_EDITMSG] max_line_length = 120 ================================================ FILE: .gitattributes ================================================ # GitHub: linguist-generated marks paths that you would like to be ignored for the repository's language statistics and hidden by default in diffs AeroSpace.xcodeproj/project.pbxproj linguist-generated=true ================================================ FILE: .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 ================================================ FILE: .github/FUNDING.yml ================================================ github: [nikitabobko] ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .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 ================================================ FILE: .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 ================================================ FILE: .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. - [ ] `./run-tests.sh` exits with non-zero exit code. - [ ] Avoid merge commits, always rebase and force push. Failure to follow the checklist with no apparent reasons will result in silent PR rejection. ================================================ FILE: .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@v6 - 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@v6 - 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' ================================================ FILE: .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 }} ================================================ FILE: .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/ ================================================ FILE: .swift-version ================================================ 6.2.4 ================================================ FILE: .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 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 ================================================ FILE: .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 - 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 ================================================ FILE: 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 = ""; }; 6606D24B4B23E9582CFA3B86 /* AeroSpace */ = {isa = PBXFileReference; lastKnownFileType = folder; name = AeroSpace; path = .; sourceTree = SOURCE_ROOT; }; 84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 8FE45A887100EB70912B07F0 /* default-config.toml */ = {isa = PBXFileReference; path = "default-config.toml"; sourceTree = ""; }; CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AeroSpace.entitlements; sourceTree = ""; }; /* 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 = ""; }; 21E15F84087042E63C0150AB /* resources */ = { isa = PBXGroup; children = ( CF85755BFF66B59A84F98262 /* AeroSpace.entitlements */, 84C35D8E25B61D4D1ADB1851 /* Assets.xcassets */, ); path = resources; sourceTree = ""; }; 393942C56466FDBBE35F9EC0 = { isa = PBXGroup; children = ( 6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */, 0E0109AE5F7881520B0D2384 /* config-examples */, 3A1FF786C84025133F96138D /* Packages */, 21E15F84087042E63C0150AB /* resources */, 62BEA6F49E6648E2EE3C208F /* Products */, ); sourceTree = ""; }; 3A1FF786C84025133F96138D /* Packages */ = { isa = PBXGroup; children = ( 6606D24B4B23E9582CFA3B86 /* AeroSpace */, ); name = Packages; sourceTree = ""; }; 62BEA6F49E6648E2EE3C208F /* Products */ = { isa = PBXGroup; children = ( 09685297933511208058F7CF /* AeroSpace.app */, ); name = Products; sourceTree = ""; }; 6F6BCFA26BF3E35072EF2C77 /* AeroSpaceApp */ = { isa = PBXGroup; children = ( 18C104E927E079E60C91AE3E /* AeroSpaceApp.swift */, ); name = AeroSpaceApp; path = Sources/AeroSpaceApp; sourceTree = ""; }; /* 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 */; } ================================================ FILE: AeroSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: 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 ================================================ FILE: 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' ================================================ FILE: 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. ================================================ FILE: Package.resolved ================================================ { "originHash" : "120ba1d30192b339dc5c9d1461d2b3d1e329a17a4867b471290c07c6b3c0cb73", "pins" : [ { "identity" : "antlr4", "kind" : "remoteSourceControl", "location" : "https://github.com/antlr/antlr4", "state" : { "revision" : "7ed420ff2c78d62883875c442d75f32e73bc86c8", "version" : "4.13.1" } }, { "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" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", "version" : "1.3.0" } }, { "identity" : "tomlkit", "kind" : "remoteSourceControl", "location" : "https://github.com/LebJe/TOMLKit", "state" : { "revision" : "404c4dd011743461bff12d00a5118d0ed59d630c", "version" : "0.5.5" } } ], "version" : 3 } ================================================ FILE: 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.git", exact: "2.0.1"), .package(url: "https://github.com/LebJe/TOMLKit.git", exact: "0.5.5"), .package(url: "https://github.com/apple/swift-collections.git", exact: "1.3.0"), .package(url: "https://github.com/soffes/HotKey.git", 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: "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"), ], ), .testTarget( name: "AppBundleTests", dependencies: [ .target(name: "AppBundle"), ], ), ], ) ================================================ FILE: 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) 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) ## 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. ## 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 ## 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) ================================================ FILE: ShellParserGenerated/.gitignore ================================================ .DS_Store /.build /Packages xcuserdata/ DerivedData/ .swiftpm/configuration/registries.json .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata .netrc ================================================ FILE: 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"), ] ), ] ) ================================================ FILE: 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.. 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) } ================================================ FILE: 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.. 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) } ================================================ FILE: 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) } } } } ================================================ FILE: 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 { scheduleRefreshSession(.globalObserver(notifName), optimisticallyPreLayoutWorkspaces: true) } else { scheduleRefreshSession(.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 runLightSession(.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 runLightSession(.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: scheduleRefreshSession(.globalObserverLeftMouseUp) } } } } } ================================================ FILE: Sources/AppBundle/command/CmdEnv.swift ================================================ import Common struct CmdEnv: ConvenienceCopyable { var windowId: UInt32? var workspaceName: String? static let defaultEnv: CmdEnv = .init() 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) } } var asMap: [String: String] { var result = [String: String]() if let windowId { result[AEROSPACE_WINDOW_ID] = windowId.description } if let workspaceName { result[AEROSPACE_WORKSPACE] = workspaceName.description } return result } } ================================================ FILE: Sources/AppBundle/command/CmdIo.swift ================================================ struct CmdStdin: ~Copyable { private var input: String = "" init(_ input: String) { self.input = input } static var emptyStdin: CmdStdin { .init("") } mutating func readAll() -> String { let result = input input = "" return result } } final class CmdIo { private var stdin: CmdStdin var stdout: [String] = [] var stderr: [String] = [] init(stdin: consuming 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 } // periphery:ignore @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 } ================================================ FILE: 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: consuming 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: consuming 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) } } ================================================ FILE: 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 .subscribe: die("subscribe is handled separately") 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 } } ================================================ FILE: 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 \(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 \(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 } } } ================================================ FILE: 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]] = [] for obj in self { var line: [Cell] = [] 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 { let value: T let rightPadding: Bool } extension String { @MainActor func expandFormatVar(obj: AeroObj) -> Result { 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_oneBased: .success(m.monitorId_oneBased.map { .int($0) } ?? .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 { 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") } } ================================================ FILE: Sources/AppBundle/command/formatToJson.swift ================================================ import Common import Foundation extension [AeroObj] { @MainActor func formatToJson(_ format: [StringInterToken], ignoreRightPaddingVar: Bool) -> Result { 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") } } ================================================ FILE: Sources/AppBundle/command/impl/BalanceSizesCommand.swift ================================================ import AppKit import Common import Foundation struct BalanceSizesCommand: Command { let args: BalanceSizesCmdArgs /*conforms*/ let 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) } } } ================================================ FILE: Sources/AppBundle/command/impl/CloseAllWindowsButCurrentCommand.swift ================================================ import AppKit import Common struct CloseAllWindowsButCurrentCommand: Command { let args: CloseAllWindowsButCurrentCmdArgs /*conforms*/ let 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 } } ================================================ FILE: Sources/AppBundle/command/impl/CloseCommand.swift ================================================ import AppKit import Common struct CloseCommand: Command { let args: CloseCmdArgs /*conforms*/ let shouldResetClosedWindowsCache = false func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool { try await allowOnlyCancellationError { @MainActor @Sendable in 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 } } } } ================================================ FILE: Sources/AppBundle/command/impl/ConfigCommand.swift ================================================ import AppKit import Common struct ConfigCommand: Command { let args: ConfigCmdArgs /*conforms*/ let 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 { 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) } } ================================================ FILE: 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 = [:] private let debugWindowsLimit = 10 enum DebugWindowsState { case recording case notRecording case recordingAborted } struct DebugWindowsCommand: Command { let args: DebugWindowsCmdArgs /*conforms*/ let 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() let windowLevel = getWindowLevel(for: window.windowId) let windowLevelJson = windowLevel?.toJson() ?? .null result["Aero.windowLevel"] = windowLevelJson 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(windowLevel) let isWindow = try await window.isWindowHeuristic(windowLevel) 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) } ================================================ FILE: Sources/AppBundle/command/impl/EnableCommand.swift ================================================ import AppKit import Common struct EnableCommand: Command { let args: EnableCmdArgs /*conforms*/ let 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 { if !args.failIfNoop { 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 } } try await activateMode(mainModeId) } else { try await activateMode(nil) } return true } } ================================================ FILE: Sources/AppBundle/command/impl/ExecAndForgetCommand.swift ================================================ import AppKit import Common struct ExecAndForgetCommand: Command { let args: ExecAndForgetCmdArgs /*conforms*/ let 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 + env.asMap process.executableURL = URL(filePath: "/bin/bash") process.arguments = ["-c", args.bashScript] return Result { try process.run() }.isSuccess } } ================================================ FILE: 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 } } ================================================ FILE: Sources/AppBundle/command/impl/FocusBackAndForthCommand.swift ================================================ import AppKit import Common struct FocusBackAndForthCommand: Command { let args: FocusBackAndForthCmdArgs /*conforms*/ let 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") } } } ================================================ FILE: Sources/AppBundle/command/impl/FocusCommand.swift ================================================ import AppKit import Common struct FocusCommand: Command { let args: FocusCmdArgs /*conforms*/ let 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 } let tilingParent: TilingContainer let index: Int if let target = center.coerce(in: workspace.workspaceMonitor.visibleRectPaddedByOuterGaps)? .findIn(tree: workspace.rootTilingContainer, virtual: true) { guard let targetCenter = try await target.getCenter() else { continue } guard let _tilingParent = target.parent as? TilingContainer else { continue } tilingParent = _tilingParent index = center.getProjection(tilingParent.orientation) >= targetCenter.getProjection(tilingParent.orientation) ? target.ownIndex.orDie() + 1 : target.ownIndex.orDie() } else { index = 0 tilingParent = workspace.rootTilingContainer } let data = window.unbindFromParent() let floatingWindowData = FloatingWindowData( window: window, center: center, parent: tilingParent, adaptiveWeight: data.adaptiveWeight, index: index, ) _floatingWindows.append(floatingWindowData) } 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") } } } ================================================ FILE: Sources/AppBundle/command/impl/FocusMonitorCommand.swift ================================================ import AppKit import Common struct FocusMonitorCommand: Command { let args: FocusMonitorCmdArgs /*conforms*/ let 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 { 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) } } ================================================ FILE: Sources/AppBundle/command/impl/FullscreenCommand.swift ================================================ import AppKit import Common struct FullscreenCommand: Command { let args: FullscreenCmdArgs /*conforms*/ let 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" ================================================ FILE: Sources/AppBundle/command/impl/JoinWithCommand.swift ================================================ import AppKit import Common struct JoinWithCommand: Command { let args: JoinWithCmdArgs /*conforms*/ let 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 } } ================================================ FILE: Sources/AppBundle/command/impl/LayoutCommand.swift ================================================ import AppKit import Common struct LayoutCommand: Command { let args: LayoutCmdArgs /*conforms*/ let 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.setAxFrame(nil, 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 } } } ================================================ FILE: Sources/AppBundle/command/impl/ListAppsCommand.swift ================================================ import AppKit import Common struct ListAppsCommand: Command { let args: ListAppsCmdArgs /*conforms*/ let 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) } } } } } ================================================ FILE: Sources/AppBundle/command/impl/ListExecEnvVarsCommand.swift ================================================ import AppKit import Common struct ListExecEnvVarsCommand: Command { let args: ListExecEnvVarsCmdArgs /*conforms*/ let shouldResetClosedWindowsCache = false func run(_ env: CmdEnv, _ io: CmdIo) -> Bool { for (key, value) in config.execConfig.envVariables { io.out("\(key)=\(value)") } return true } } ================================================ FILE: Sources/AppBundle/command/impl/ListModesCommand.swift ================================================ import AppKit import Common struct ListModesCommand: Command { let args: ListModesCmdArgs /*conforms*/ let 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) } } } ================================================ FILE: Sources/AppBundle/command/impl/ListMonitorsCommand.swift ================================================ import AppKit import Common struct ListMonitorsCommand: Command { let args: ListMonitorsCmdArgs /*conforms*/ let 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) } } } } } ================================================ FILE: Sources/AppBundle/command/impl/ListWindowsCommand.swift ================================================ import AppKit import Common struct ListWindowsCommand: Command { let args: ListWindowsCmdArgs /*conforms*/ let 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 = 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 = 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) } } } } } ================================================ FILE: Sources/AppBundle/command/impl/ListWorkspacesCommand.swift ================================================ import AppKit import Common struct ListWorkspacesCommand: Command { let args: ListWorkspacesCmdArgs /*conforms*/ let 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 = 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 { var requested: Set = [] 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 [] } } } } ================================================ FILE: 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*/ let 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 { if !args.failIfNoop { 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 } } ================================================ FILE: Sources/AppBundle/command/impl/MacosNativeMinimizeCommand.swift ================================================ import AppKit import Common /// See: MacosNativeFullscreenCommand. Problem ID-B6E178F2 struct MacosNativeMinimizeCommand: Command { let args: MacosNativeMinimizeCmdArgs /*conforms*/ let 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 } } } ================================================ FILE: Sources/AppBundle/command/impl/ModeCommand.swift ================================================ import AppKit import Common struct ModeCommand: Command { let args: ModeCmdArgs /*conforms*/ let shouldResetClosedWindowsCache = false func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool { try await activateMode(args.targetMode.val) return true } } ================================================ FILE: Sources/AppBundle/command/impl/MoveCommand.swift ================================================ import AppKit import Common struct MoveCommand: Command { let args: MoveCmdArgs /*conforms*/ let 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, args, direction) } } } @MainActor private func hitAllMonitorsOuterFrameBoundaries( _ window: Window, _ workspace: Workspace, _ 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.cases { 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 } } @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) } } } } ================================================ FILE: Sources/AppBundle/command/impl/MoveMouseCommand.swift ================================================ import AppKit import Common struct MoveMouseCommand: Command { let args: MoveMouseCmdArgs /*conforms*/ let 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) { if !args.failIfNoop { 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) { if !args.failIfNoop { 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 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 } } ================================================ FILE: Sources/AppBundle/command/impl/MoveNodeToMonitorCommand.swift ================================================ import AppKit import Common struct MoveNodeToMonitorCommand: Command { let args: MoveNodeToMonitorCmdArgs /*conforms*/ let 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)" } ================================================ FILE: 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 { if !failIfNoop { 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 } ================================================ FILE: Sources/AppBundle/command/impl/MoveWorkspaceToMonitorCommand.swift ================================================ import AppKit import Common struct MoveWorkspaceToMonitorCommand: Command { let args: MoveWorkspaceToMonitorCmdArgs /*conforms*/ let 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_oneBased == prevMonitor.monitorId_oneBased { 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) } } } ================================================ FILE: Sources/AppBundle/command/impl/ReloadConfigCommand.swift ================================================ import AppKit import Common struct ReloadConfigCommand: Command { let args: ReloadConfigCmdArgs /*conforms*/ let shouldResetClosedWindowsCache = false func run(_ env: CmdEnv, _ io: CmdIo) async throws -> Bool { var stdout = "" let isOk = try await reloadConfig(args: args, stdout: &stdout) if !stdout.isEmpty { io.out(stdout) } return isOk } } @MainActor func reloadConfig(forceConfigUrl: URL? = nil) async throws -> Bool { var devNull = "" return try await reloadConfig(forceConfigUrl: forceConfigUrl, stdout: &devNull) } @MainActor func reloadConfig( args: ReloadConfigCmdArgs = ReloadConfigCmdArgs(rawArgs: []), forceConfigUrl: URL? = nil, stdout: inout String, ) async throws -> Bool { let result: Bool switch readConfig(forceConfigUrl: forceConfigUrl) { case .success(let (parsedConfig, url)): if !args.dryRun { resetHotKeys() config = parsedConfig configUrl = url try await activateMode(activeMode) syncStartAtLogin() MessageModel.shared.message = nil } result = true case .failure(let msg): stdout.append(msg) if !args.noGui { Task { @MainActor in MessageModel.shared.message = Message(description: "AeroSpace Config Error", body: msg) } } result = false } if !args.dryRun { syncConfigFileWatcher() } return result } ================================================ FILE: Sources/AppBundle/command/impl/ResizeCommand.swift ================================================ import AppKit import Common struct ResizeCommand: Command { // todo cover with tests let args: ResizeCmdArgs /*conforms*/ let 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 } } ================================================ FILE: Sources/AppBundle/command/impl/SplitCommand.swift ================================================ import AppKit import Common struct SplitCommand: Command { let args: SplitCmdArgs /*conforms*/ let 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 } } } ================================================ FILE: Sources/AppBundle/command/impl/SummonWorkspaceCommand.swift ================================================ import AppKit import Common struct SummonWorkspaceCommand: Command { let args: SummonWorkspaceCmdArgs /*conforms*/ let 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 { if !args.failIfNoop { 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") } } } ================================================ FILE: 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 } } ================================================ FILE: Sources/AppBundle/command/impl/TriggerBindingCommand.swift ================================================ import AppKit import Common struct TriggerBindingCommand: Command { let args: TriggerBindingCmdArgs /*conforms*/ let 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: ","))") } } } ================================================ FILE: Sources/AppBundle/command/impl/VolumeCommand.swift ================================================ import AppKit import Common import ISSoundAdditions struct VolumeCommand: Command { let args: VolumeCmdArgs /*conforms*/ let 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 args.gui, let volume = try? Sound.output.readVolume() { VolumePanel.shared.update(with: Sound.output.isMuted ? 0 : volume) } return true } } ================================================ FILE: Sources/AppBundle/command/impl/WorkspaceBackAndForthCommand.swift ================================================ import AppKit import Common struct WorkspaceBackAndForthCommand: Command { let args: WorkspaceBackAndForthCmdArgs /*conforms*/ let shouldResetClosedWindowsCache = false func run(_ env: CmdEnv, _ io: CmdIo) -> Bool { return prevFocusedWorkspace?.focusWorkspace() != nil } } ================================================ FILE: Sources/AppBundle/command/impl/WorkspaceCommand.swift ================================================ import AppKit import Common import Foundation struct WorkspaceCommand: Command { let args: WorkspaceCmdArgs /*conforms*/ let 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 { if !args.failIfNoop { 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 } ================================================ FILE: Sources/AppBundle/command/parseCommand.swift ================================================ import Common import TOMLKit func parseCommand(_ raw: String) -> ParsedCmd { 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 { 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)'" } } ================================================ FILE: Sources/AppBundle/config/Config.swift ================================================ import AppKit import Common import HotKey import OrderedCollections 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(Result { try String(contentsOf: defaultConfigUrl, encoding: .utf8) }.getOrDie()) 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 configVersion: Int = 1 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 autoReloadConfig: Bool = false var automaticallyUnhideMacosHiddenApps: Bool = false var accordionPadding: Int = 30 var enableNormalizationOppositeOrientationForNestedContainers: Bool = true var persistentWorkspaces: OrderedSet = [] 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 onModeChanged: [any Command] = [] } enum DefaultContainerOrientation: String { case horizontal, vertical, auto } ================================================ FILE: 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 } } } ================================================ FILE: Sources/AppBundle/config/ConfigFileWatcher.swift ================================================ import Common import Foundation private struct ConfigFileWatcher: ~Copyable { private let source: DispatchSourceFileSystemObject private let fd: Int32 init?(url: URL, onChange: @escaping @MainActor () -> Void) { fd = open(url.path, O_EVTONLY) if fd < 0 { return nil } source = DispatchSource.makeFileSystemObjectSource( fileDescriptor: fd, eventMask: [.write, .delete, .rename, .revoke], queue: .main, ) source.setEventHandler { MainActor.checkIsolated { onChange() } } source.setCancelHandler { [fd] in close(fd) } source.activate() } deinit { source.cancel() } } @MainActor private var currentWatcher: ConfigFileWatcher? = nil @MainActor private var debounceTask: Task? = nil private let debounceDelay: Duration = .milliseconds(200) @MainActor func syncConfigFileWatcher() { currentWatcher = nil if !config.autoReloadConfig { return } currentWatcher = ConfigFileWatcher(url: configUrl) { debounceTask?.cancel() debounceTask = Task { try await Task.sleep(for: debounceDelay) if let token: RunSessionGuard = .isServerEnabled { try await runLightSession(.configAutoReload, token) { _ = try await reloadConfig() } } } } } ================================================ FILE: Sources/AppBundle/config/DynamicConfigValue.swift ================================================ import Common import TOMLKit struct PerMonitorValue: Equatable { let description: MonitorDescription let value: Value } extension PerMonitorValue: Sendable where Value: Sendable {} enum DynamicConfigValue: Equatable { case constant(Value) case perMonitor([PerMonitorValue], 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( _ raw: TOMLValueConvertible, _ valueType: T.Type, _ fallback: T, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError], ) -> DynamicConfigValue { 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] = 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(_ array: TOMLArray, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [PerMonitorValue] { array.enumerated().compactMap { (index: Int, raw: TOMLValueConvertible) -> PerMonitorValue? 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) } } ================================================ FILE: 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?) async throws { 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 { broadcastEvent(.bindingTriggered( mode: activeMode, binding: binding.descriptionWithKeyNotation, )) try await runLightSession(.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 } } let oldMode = activeMode activeMode = targetMode if oldMode != targetMode { broadcastEvent(.modeChanged(mode: targetMode)) if !config.onModeChanged.isEmpty { guard let token: RunSessionGuard = .isServerEnabled else { return } try await runLightSession(.onModeChanged, token) { _ = try await config.onModeChanged.runCmdSeq(.defaultEnv, .emptyStdin) } } } } 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 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 = rawKeys.dropLast() .mapAllOrFailure { modifiersMap[String($0)].orFailure(.semantic(backtrace, "Can't parse modifiers in '\(raw)' binding")) } .map { NSEvent.ModifierFlags($0) } let key: ParsedToml = 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)) } } } ================================================ FILE: Sources/AppBundle/config/Mode.swift ================================================ import Common import HotKey import TOMLKit struct Mode: ConvenienceCopyable, Equatable, Sendable { var bindings: [String: HotkeyBinding] static let zero = Mode(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 } ================================================ FILE: 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 ================================================ FILE: Sources/AppBundle/config/parseConfig.swift ================================================ import AppKit import Common import HotKey import TOMLKit import OrderedCollections @MainActor func readConfig(forceConfigUrl: URL? = nil) -> Result<(Config, URL), String> { let configUrl: URL if let forceConfigUrl { configUrl = forceConfigUrl } else { switch findCustomConfigUrl() { case .file(let url): configUrl = url case .noCustomConfigExists: configUrl = defaultConfigUrl case .ambiguousConfigError(let candidates): let msg = """ Ambiguous config error. Several configs found: \(candidates.map(\.path).joined(separator: "\n")) """ return .failure(msg) } } let (parsedConfig, errors) = (try? String(contentsOf: configUrl, encoding: .utf8)).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) where backtrace.description.isEmpty: message case .semantic(let backtrace, let message): "\(backtrace): \(message)" case .syntax(let message): message } } } typealias ParsedToml = Result 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: Sendable { associatedtype T associatedtype S where S: ConvenienceCopyable var keyPath: SendableWritableKeyPath { get } var parse: @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> ParsedToml { get } } struct Parser: ParserProtocol { let keyPath: SendableWritableKeyPath let parse: @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> ParsedToml init(_ keyPath: SendableWritableKeyPath, _ parse: @escaping @Sendable (TOMLValueConvertible, TomlBacktrace, inout [TomlParseError]) -> T) { self.keyPath = keyPath self.parse = { raw, backtrace, errors -> ParsedToml in .success(parse(raw, backtrace, &errors)) } } init(_ keyPath: SendableWritableKeyPath, _ parse: @escaping @Sendable (TOMLValueConvertible, TomlBacktrace) -> ParsedToml) { self.keyPath = keyPath self.parse = { raw, backtrace, _ -> ParsedToml in parse(raw, backtrace) } } } private let keyMappingConfigRootKey = "key-mapping" private let modeConfigRootKey = "mode" private let persistentWorkspacesKey = "persistent-workspaces" // 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-version": Parser(\.configVersion, parseConfigVersion), "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-mode-changed": Parser(\.onModeChanged) { 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), "auto-reload-config": Parser(\.autoReloadConfig, parseBool), "automatically-unhide-macos-hidden-apps": Parser(\.automaticallyUnhideMacosHiddenApps, parseBool), "accordion-padding": Parser(\.accordionPadding, parseInt), persistentWorkspacesKey: Parser(\.persistentWorkspaces, parsePersistentWorkspaces), "exec-on-workspace-change": Parser(\.execOnWorkspaceChange, parseArrayOfStrings), "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 { 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 } // Parse modeConfigRootKey after keyMappingConfigRootKey if let modes = rawTable[modeConfigRootKey].flatMap({ parseModes($0, .rootKey(modeConfigRootKey), &errors, config.keyMapping.resolve()) }) { config.modes = modes } if config.configVersion <= 1 { if rawTable.contains(key: persistentWorkspacesKey) { errors += [.semantic(.rootKey(persistentWorkspacesKey), "This config option is only available since 'config-version = 2'")] } config.persistentWorkspaces = (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) .toOrderedSet() } 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( _ _: TOMLValueConvertible, _ backtrace: TomlBacktrace, ) -> ParsedToml { let msg = "Deprecated. Please drop it from the config. See https://github.com/nikitabobko/AeroSpace/issues/96" return .failure(.semantic(backtrace, msg)) } func parseConfigVersion(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { let min = 1 let max = 2 return parseInt(raw, backtrace) .filter(.semantic(backtrace, "Must be in [\(min), \(max)] range")) { (min ... max).contains($0) } } func parseInt(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { raw.int.orFailure(expectedActualTypeError(expected: .int, actual: raw.type, backtrace)) } func parseString(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { raw.string.orFailure(expectedActualTypeError(expected: .string, actual: raw.type, backtrace)) } func parseSimpleType(_ 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 { raw.array.orFailure(expectedActualTypeError(expected: .array, actual: raw.type, backtrace)) } func parseTable( _ raw: TOMLValueConvertible, _ initial: T, _ fieldsParser: [String: any ParserProtocol], _ 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 { 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 { parseString(raw, backtrace) .flatMap { $0.parseLayout().orFailure(.semantic(backtrace, "Can't parse layout '\($0)'")) } } private func skipParsing(_ value: T) -> @Sendable (_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { { _, _ in .success(value) } } private func parsePersistentWorkspaces(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml> { parseArrayOfStrings(raw, backtrace) .flatMap { arr in let set = arr.toOrderedSet() return set.count == arr.count ? .success(set) : .failure(.semantic(backtrace, "Contains duplicated workspace names")) } } private func parseArrayOfStrings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml<[String]> { parseTomlArray(raw, backtrace) .flatMap { arr in arr.enumerated().mapAllOrFailure { (index, elem) in parseString(elem, backtrace + .index(index)) } } } private func parseDefaultContainerOrientation(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { 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 { mapError { .semantic(backtrace, $0) } } } func parseBool(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { raw.bool.orFailure(expectedActualTypeError(expected: .bool, actual: raw.type, backtrace)) } struct TomlBacktrace: CustomStringConvertible, Equatable { private var path: [TomlBacktraceItem] = [] private init(_ path: [TomlBacktraceItem]) { check(path.first?.isKey != false, "Tried to construct invalid TOML path: \(path)") self.path = path } static func rootKey(_ key: String) -> Self { .init([.key(key)]) } static let emptyRoot: Self = .init([]) var description: String { var result = "" for (i, elem) in path.enumerated() { switch elem { case .key(let rootKey) where i == 0: result += rootKey case .key(let key): result += ".\(key)" case .index(let index): result += "[\(index)]" } } return result } var isRootKey: Bool { path.singleOrNil().map(\.isKey) == true } static func + (lhs: Self, rhs: TomlBacktraceItem) -> Self { var result = lhs result.path += [rhs] return result } } enum TomlBacktraceItem: Equatable { case key(String) case index(Int) var isKey: Bool { switch self { case .key: true case .index: false } } } extension TOMLTable { func parseTable( _ initial: T, _ fieldsParser: [String: any ParserProtocol], _ 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)) } ================================================ FILE: 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] = [ "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 } ================================================ FILE: Sources/AppBundle/config/parseGaps.swift ================================================ import Common import TOMLKit struct Gaps: ConvenienceCopyable, Equatable, Sendable { var inner: Inner var outer: Outer static let zero = Gaps(inner: .zero, outer: .zero) struct Inner: ConvenienceCopyable, Equatable, Sendable { var vertical: DynamicConfigValue var horizontal: DynamicConfigValue static let zero = Inner(vertical: 0, horizontal: 0) init(vertical: Int, horizontal: Int) { self.vertical = .constant(vertical) self.horizontal = .constant(horizontal) } init(vertical: DynamicConfigValue, horizontal: DynamicConfigValue) { self.vertical = vertical self.horizontal = horizontal } } struct Outer: ConvenienceCopyable, Equatable, Sendable { var left: DynamicConfigValue var bottom: DynamicConfigValue var top: DynamicConfigValue var right: DynamicConfigValue static let zero = Outer(left: 0, bottom: 0, top: 0, right: 0) init(left: Int, bottom: Int, top: Int, right: Int) { self.left = .constant(left) self.bottom = .constant(bottom) self.top = .constant(top) self.right = .constant(right) } init(left: DynamicConfigValue, bottom: DynamicConfigValue, top: DynamicConfigValue, right: DynamicConfigValue) { self.left = left self.bottom = bottom self.top = top self.right = right } } } struct ResolvedGaps { let inner: Inner let outer: Outer struct Inner { let vertical: Int let horizontal: Int func get(_ orientation: Orientation) -> Int { orientation == .h ? horizontal : vertical } } struct Outer { let left: Int let bottom: Int let top: Int let right: Int } init(gaps: Gaps, monitor: any Monitor) { inner = .init( vertical: gaps.inner.vertical.getValue(for: monitor), horizontal: gaps.inner.horizontal.getValue(for: monitor), ) outer = .init( left: gaps.outer.left.getValue(for: monitor), bottom: gaps.outer.bottom.getValue(for: monitor), top: gaps.outer.top.getValue(for: monitor), right: gaps.outer.right.getValue(for: monitor), ) } } private let gapsParser: [String: any ParserProtocol] = [ "inner": Parser(\.inner, parseInner), "outer": Parser(\.outer, parseOuter), ] private let innerParser: [String: any ParserProtocol] = [ "vertical": Parser(\.vertical) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, "horizontal": Parser(\.horizontal) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, ] private let outerParser: [String: any ParserProtocol] = [ "left": Parser(\.left) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, "bottom": Parser(\.bottom) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, "top": Parser(\.top) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, "right": Parser(\.right) { value, backtrace, errors in parseDynamicValue(value, Int.self, 0, backtrace, &errors) }, ] func parseGaps(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> Gaps { parseTable(raw, .zero, gapsParser, backtrace, &errors) } func parseInner(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> Gaps.Inner { parseTable(raw, Gaps.Inner.zero, innerParser, backtrace, &errors) } func parseOuter(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> Gaps.Outer { parseTable(raw, Gaps.Outer.zero, outerParser, backtrace, &errors) } ================================================ FILE: Sources/AppBundle/config/parseKeyMapping.swift ================================================ import Common import HotKey import TOMLKit private let keyMappingParser: [String: any ParserProtocol] = [ "preset": Parser(\.preset, parsePreset), "key-notation-to-key-code": Parser(\.rawKeyNotationToKeyCode, parseKeyNotationToKeyCode), ] struct KeyMapping: ConvenienceCopyable, Equatable, Sendable { enum Preset: String, CaseIterable, Sendable { case qwerty, dvorak, colemak } init( preset: Preset = .qwerty, rawKeyNotationToKeyCode: [String: Key] = [:], ) { self.preset = preset self.rawKeyNotationToKeyCode = rawKeyNotationToKeyCode } fileprivate var preset: Preset = .qwerty fileprivate var rawKeyNotationToKeyCode: [String: Key] = [:] func resolve() -> [String: Key] { getKeysPreset(preset) + rawKeyNotationToKeyCode } } func parseKeyMapping(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> KeyMapping { parseTable(raw, KeyMapping(), keyMappingParser, backtrace, &errors) } private func parsePreset(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { parseString(raw, backtrace).flatMap { parseEnum($0, KeyMapping.Preset.self).toParsedToml(backtrace) } } private func parseKeyNotationToKeyCode(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [String: Key] { var result: [String: Key] = [:] guard let table = raw.table else { errors.append(expectedActualTypeError(expected: .table, actual: raw.type, backtrace)) return result } for (key, value): (String, TOMLValueConvertible) in table { if isValidKeyNotation(key) { let backtrace = backtrace + .key(key) if let value = parseString(value, backtrace).getOrNil(appendErrorTo: &errors) { if let value = keyNotationToKeyCode[value] { result[key] = value } else { errors.append(.semantic(backtrace, "'\(value)' is invalid key code")) } } } else { errors.append(.semantic(backtrace, "'\(key)' is invalid key notation")) } } return result } private func isValidKeyNotation(_ str: String) -> Bool { str.rangeOfCharacter(from: .whitespacesAndNewlines) == nil && !str.contains("-") } ================================================ FILE: Sources/AppBundle/config/parseOnWindowDetected.swift ================================================ import Common import TOMLKit struct WindowDetectedCallback: ConvenienceCopyable, Equatable { var matcher: WindowDetectedCallbackMatcher = WindowDetectedCallbackMatcher() var checkFurtherCallbacks: Bool = false var rawRun: [any Command]? = nil var run: [any Command] { rawRun ?? dieT("ID-46D063B2 should have discarded nil") } var debugJson: Json { var result: [String: Json] = [:] result["matcher"] = matcher.debugJson if let commands = rawRun { result["commands"] = .string(commands.prettyDescription) } return .dict(result) } static func == (lhs: WindowDetectedCallback, rhs: WindowDetectedCallback) -> Bool { return lhs.matcher == rhs.matcher && lhs.checkFurtherCallbacks == rhs.checkFurtherCallbacks && zip(lhs.run, rhs.run).allSatisfy { $0.equals($1) } } } struct WindowDetectedCallbackMatcher: ConvenienceCopyable, Equatable { var appId: String? var appNameRegexSubstring: Regex? var windowTitleRegexSubstring: Regex? var workspace: String? var duringAeroSpaceStartup: Bool? var debugJson: Json { var resultParts: [String] = [] if let appId { resultParts.append("appId=\"\(appId)\"") } if appNameRegexSubstring != nil { resultParts.append("appNameRegexSubstrin=Regex") } if windowTitleRegexSubstring != nil { resultParts.append("windowTitleRegexSubstring=Regex") } if let workspace { resultParts.append("workspace=\"\(workspace)\"") } if let duringAeroSpaceStartup { resultParts.append("duringAeroSpaceStartup=\(duringAeroSpaceStartup)") } return .string(resultParts.joined(separator: ", ")) } static func == (lhs: WindowDetectedCallbackMatcher, rhs: WindowDetectedCallbackMatcher) -> Bool { check( lhs.appNameRegexSubstring == nil && lhs.windowTitleRegexSubstring == nil && rhs.appNameRegexSubstring == nil && rhs.windowTitleRegexSubstring == nil, ) return lhs.appId == rhs.appId } } private let windowDetectedParser: [String: any ParserProtocol] = [ "if": Parser(\.matcher, parseMatcher), "check-further-callbacks": Parser(\.checkFurtherCallbacks, parseBool), "run": Parser(\.rawRun, upcast { parseCommandOrCommands($0).toParsedToml($1) }), ] private let matcherParsers: [String: any ParserProtocol] = [ "app-id": Parser(\.appId, upcast(parseString)), "workspace": Parser(\.workspace, upcast(parseString)), "app-name-regex-substring": Parser(\.appNameRegexSubstring, upcast(parseCasInsensitiveRegex)), "window-title-regex-substring": Parser(\.windowTitleRegexSubstring, upcast(parseCasInsensitiveRegex)), "during-aerospace-startup": Parser(\.duringAeroSpaceStartup, upcast(parseBool)), ] private func upcast(_ fun: @escaping @Sendable (TOMLValueConvertible, TomlBacktrace) -> ParsedToml) -> @Sendable (TOMLValueConvertible, TomlBacktrace) -> ParsedToml { { fun($0, $1).map { $0 } } } func parseOnWindowDetectedArray(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [WindowDetectedCallback] { if let array = raw.array { return array.enumerated().map { (index, raw) in parseWindowDetectedCallback(raw, backtrace + .index(index), &errors) }.filterNotNil() } else { errors += [expectedActualTypeError(expected: .array, actual: raw.type, backtrace)] return [] } } private func parseCasInsensitiveRegex(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml> { parseString(raw, backtrace).flatMap { parseCaseInsensitiveRegex($0).toParsedToml(backtrace) } } private func parseMatcher(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> WindowDetectedCallbackMatcher { parseTable(raw, WindowDetectedCallbackMatcher(), matcherParsers, backtrace, &errors) } private func parseWindowDetectedCallback(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> WindowDetectedCallback? { var myErrors: [TomlParseError] = [] let callback = parseTable(raw, WindowDetectedCallback(), windowDetectedParser, backtrace, &myErrors) if callback.rawRun == nil { // ID-46D063B2 myErrors.append(.semantic(backtrace, "'run' is mandatory key")) } if !myErrors.isEmpty { errors += myErrors return nil } return callback } ================================================ FILE: Sources/AppBundle/config/parseWorkspaceToMonitorAssignment.swift ================================================ import Common import TOMLKit func parseWorkspaceToMonitorAssignment(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [String: [MonitorDescription]] { guard let rawTable = raw.table else { errors += [expectedActualTypeError(expected: .table, actual: raw.type, backtrace)] return [:] } var result: [String: [MonitorDescription]] = [:] for (workspaceName, rawMonitorDescription) in rawTable { result[workspaceName] = parseMonitorDescriptions(rawMonitorDescription, backtrace + .key(workspaceName), &errors) } return result } func parseMonitorDescriptions(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace, _ errors: inout [TomlParseError]) -> [MonitorDescription] { if let array = raw.array { return array.enumerated() .map { (index, rawDesc) in parseMonitorDescription(rawDesc, backtrace + .index(index)).getOrNil(appendErrorTo: &errors) } .filterNotNil() } else { return parseMonitorDescription(raw, backtrace).getOrNil(appendErrorTo: &errors).asList() } } func parseMonitorDescription(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedToml { let rawString: String if let string = raw.string { rawString = string } else if let int = raw.int { rawString = String(int) } else { return .failure(expectedActualTypeError(expected: [.string, .int], actual: raw.type, backtrace)) } return parseMonitorDescription(rawString).toParsedToml(backtrace) } ================================================ FILE: 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) } ================================================ FILE: Sources/AppBundle/focus.swift ================================================ import AppKit import Common enum EffectiveLeaf { case window(Window) case emptyWorkspace(Workspace) } extension LiveFocus { var asLeaf: EffectiveLeaf { if let windowOrNil { .window(windowOrNil) } else { .emptyWorkspace(workspace) } } } /// This object should be only passed around but never memorized /// Alternative name: ResolvedFocus struct LiveFocus: AeroAny, Equatable { let windowOrNil: Window? var workspace: Workspace @MainActor fileprivate var frozen: FrozenFocus { return FrozenFocus( windowId: windowOrNil?.windowId, workspaceName: workspace.name, monitorId_oneBased: workspace.workspaceMonitor.monitorId_oneBased ?? 0, ) } } /// "old", "captured", "frozen in time" Focus /// It's safe to keep a hard reference to this object. /// Unlike in LiveFocus, information inside FrozenFocus isn't guaranteed to be self-consistent. /// window - workspace - monitor relation could change since the moment object was created private struct FrozenFocus: AeroAny, Equatable, Sendable { let windowId: UInt32? let workspaceName: String // monitorId is not part of the focus. We keep it here only for 'on-focused-monitor-changed' to work let monitorId_oneBased: Int @MainActor var live: LiveFocus { // Important: don't access focus.monitorId here. monitorId is not part of the focus. Always prefer workspace let window: Window? = windowId.flatMap { Window.get(byId: $0) } let workspace = Workspace.get(byName: workspaceName) let workspaceFocus = workspace.toLiveFocus() let windowFocus = window?.toLiveFocusOrNil() ?? workspaceFocus return workspaceFocus.workspace != windowFocus.workspace ? workspaceFocus // If window and workspace become separated prefer workspace : windowFocus } } @MainActor private var _focus: FrozenFocus = { let monitor = mainMonitor return FrozenFocus(windowId: nil, workspaceName: monitor.activeWorkspace.name, monitorId_oneBased: monitor.monitorId_oneBased ?? 0) }() /// Global focus. /// Commands must be cautious about accessing this property directly. There are legitimate cases. /// But, in general, commands must firstly check --window-id, --workspace, AEROSPACE_WINDOW_ID env and /// AEROSPACE_WORKSPACE env before accessing the global focus. @MainActor var focus: LiveFocus { _focus.live } @MainActor func setFocus(to newFocus: LiveFocus) -> Bool { if _focus == newFocus.frozen { return true } let oldFocus = focus // Normalize mruWindow when focus away from a workspace if oldFocus.workspace != newFocus.workspace { oldFocus.windowOrNil?.markAsMostRecentChild() } _focus = newFocus.frozen let status = newFocus.workspace.workspaceMonitor.setActiveWorkspace(newFocus.workspace) newFocus.windowOrNil?.markAsMostRecentChild() return status } extension Window { @MainActor func focusWindow() -> Bool { if let focus = toLiveFocusOrNil() { return setFocus(to: focus) } else { // todo We should also exit-native-hidden/unminimize[/exit-native-fullscreen?] window if we want to fix ID-B6E178F2 // and retry to focus the window. Otherwise, it's not possible to focus minimized/hidden windows return false } } @MainActor func toLiveFocusOrNil() -> LiveFocus? { visualWorkspace.map { LiveFocus(windowOrNil: self, workspace: $0) } } } extension Workspace { @MainActor func focusWorkspace() -> Bool { setFocus(to: toLiveFocus()) } func toLiveFocus() -> LiveFocus { // todo unfortunately mostRecentWindowRecursive may recursively reach empty rootTilingContainer // while floating or macos unconventional windows might be presented if let wd = mostRecentWindowRecursive ?? anyLeafWindowRecursive { LiveFocus(windowOrNil: wd, workspace: self) } else { LiveFocus(windowOrNil: nil, workspace: self) // emptyWorkspace } } } @MainActor private var _lastKnownFocus: FrozenFocus = _focus // Used by workspace-back-and-forth @MainActor var _prevFocusedWorkspaceName: String? = nil { didSet { prevFocusedWorkspaceDate = .now } } @MainActor var prevFocusedWorkspaceDate: Date = .distantPast @MainActor var prevFocusedWorkspace: Workspace? { _prevFocusedWorkspaceName.map { Workspace.get(byName: $0) } } // Used by focus-back-and-forth @MainActor private var _prevFocus: FrozenFocus? = nil @MainActor var prevFocus: LiveFocus? { _prevFocus?.live.takeIf { $0 != focus } } @MainActor private var onFocusChangedRecursionGuard = false // Should be called in refreshSession @MainActor func checkOnFocusChangedCallbacks() { if refreshSessionEvent?.isStartup == true { return } let focus = focus let frozenFocus = focus.frozen var hasFocusChanged = false var hasFocusedWorkspaceChanged = false var hasFocusedMonitorChanged = false if frozenFocus != _lastKnownFocus { _prevFocus = _lastKnownFocus hasFocusChanged = true } if frozenFocus.workspaceName != _lastKnownFocus.workspaceName { _prevFocusedWorkspaceName = _lastKnownFocus.workspaceName hasFocusedWorkspaceChanged = true } if frozenFocus.monitorId_oneBased != _lastKnownFocus.monitorId_oneBased { hasFocusedMonitorChanged = true } _lastKnownFocus = frozenFocus if onFocusChangedRecursionGuard { return } onFocusChangedRecursionGuard = true defer { onFocusChangedRecursionGuard = false } if hasFocusChanged { onFocusChanged(focus) } if let _prevFocusedWorkspaceName, hasFocusedWorkspaceChanged { onWorkspaceChanged(_prevFocusedWorkspaceName, frozenFocus.workspaceName) } if hasFocusedMonitorChanged { onFocusedMonitorChanged(focus) } } @MainActor private func onFocusedMonitorChanged(_ focus: LiveFocus) { broadcastEvent(.focusedMonitorChanged( workspace: focus.workspace.name, monitorId_oneBased: focus.workspace.workspaceMonitor.monitorId_oneBased ?? 0, )) if config.onFocusedMonitorChanged.isEmpty { return } guard let token: RunSessionGuard = .isServerEnabled else { return } // todo potential optimization: don't run runSession if we are already in runSession Task { try await runLightSession(.onFocusedMonitorChanged, token) { _ = try await config.onFocusedMonitorChanged.runCmdSeq(.defaultEnv.withFocus(focus), .emptyStdin) } } } @MainActor private func onFocusChanged(_ focus: LiveFocus) { broadcastEvent(.focusChanged( windowId: focus.windowOrNil?.windowId, workspace: focus.workspace.name, )) if config.onFocusChanged.isEmpty { return } guard let token: RunSessionGuard = .isServerEnabled else { return } // todo potential optimization: don't run runSession if we are already in runSession Task { try await runLightSession(.onFocusChanged, token) { _ = try await config.onFocusChanged.runCmdSeq(.defaultEnv.withFocus(focus), .emptyStdin) } } } @MainActor private func onWorkspaceChanged(_ oldWorkspace: String, _ newWorkspace: String) { broadcastEvent(.workspaceChanged( workspace: newWorkspace, prevWorkspace: oldWorkspace, )) if let exec = config.execOnWorkspaceChange.first { let process = Process() process.executableURL = URL(filePath: exec) process.arguments = Array(config.execOnWorkspaceChange.dropFirst()) var environment = config.execConfig.envVariables environment["AEROSPACE_FOCUSED_WORKSPACE"] = newWorkspace environment["AEROSPACE_PREV_WORKSPACE"] = oldWorkspace environment[AEROSPACE_WORKSPACE] = newWorkspace process.environment = environment _ = Result { try process.run() } } } ================================================ FILE: Sources/AppBundle/focusCache.swift ================================================ @MainActor private var lastKnownNativeFocusedWindowId: UInt32? = nil /// The data should flow (from nativeFocused to focused) and /// (from nativeFocused to lastKnownNativeFocusedWindowId) /// Alternative names: takeFocusFromMacOs, syncFocusFromMacOs @MainActor func updateFocusCache(_ nativeFocused: Window?) { if nativeFocused?.parent is MacosPopupWindowsContainer { return } if nativeFocused?.windowId != lastKnownNativeFocusedWindowId { _ = nativeFocused?.focusWindow() lastKnownNativeFocusedWindowId = nativeFocused?.windowId } nativeFocused?.macAppUnsafe.lastNativeFocusedWindowId = nativeFocused?.windowId } ================================================ FILE: Sources/AppBundle/getNativeFocusedWindow.swift ================================================ import AppKit import Common @MainActor var appForTests: (any AbstractApp)? = nil @MainActor private var focusedApp: (any AbstractApp)? { get async throws { if isUnitTest { return appForTests } else { check(appForTests == nil) return try await NSWorkspace.shared.frontmostApplication.flatMapAsync { @MainActor @Sendable in try await MacApp.getOrRegister($0) } } } } @MainActor func getNativeFocusedWindow() async throws -> Window? { try await focusedApp?.getFocusedWindow() } ================================================ FILE: Sources/AppBundle/initAppBundle.swift ================================================ import AppKit import Common import Foundation @MainActor public func initAppBundle() { Task { initTerminationHandler() isCli = false initServerArgs() if isDebug { await toggleReleaseServerIfDebug(.off) interceptTermination(SIGINT) interceptTermination(SIGKILL) } if try await !reloadConfig() { var out = "" check( try await reloadConfig(forceConfigUrl: defaultConfigUrl, stdout: &out), """ Can't load default config. Your installation is probably corrupted. Please don't modify '\(defaultConfigUrl)' \(out) """, ) } checkAccessibilityPermissions() startUnixSocketServer() GlobalObserver.initObserver() Workspace.garbageCollectUnusedWorkspaces() // init workspaces _ = Workspace.all.first?.focusWorkspace() try await runRefreshSessionBlocking(.startup, layoutWorkspaces: false) try await runLightSession(.startup, .forceRun) { smartLayoutAtStartup() _ = try await config.afterStartupCommand.runCmdSeq(.defaultEnv, .emptyStdin) } } } @MainActor private func smartLayoutAtStartup() { let workspace = focus.workspace let root = workspace.rootTilingContainer if root.children.count <= 3 { root.layout = .tiles } else { root.layout = .accordion } } @TaskLocal var _isStartup: Bool? = false var isStartup: Bool { _isStartup ?? dieT("isStartup is not initialized") } struct ServerArgs: Sendable { var configLocation: String? = nil var isReadOnly: Bool = false } private let serverHelp = """ USAGE: \(CommandLine.arguments.first ?? "AeroSpace.app/Contents/MacOS/AeroSpace") [] OPTIONS: -h, --help Print help -v, --version Print AeroSpace.app version --config-path Config path. It will take priority over ~/.aerospace.toml and ${XDG_CONFIG_HOME}/aerospace/aerospace.toml --read-only Disable window management. Useful if you want to use only debug-windows or other query commands. """ nonisolated(unsafe) private var _serverArgs = ServerArgs() var serverArgs: ServerArgs { _serverArgs } private func initServerArgs() { let args = CommandLine.arguments.slice(1...) ?? [] if args.contains(where: { $0 == "-h" || $0 == "--help" }) { exit(0, out: serverHelp) } var index = 0 while index < args.count { let current = args[index] index += 1 switch current { case "--version", "-v": exit(0, out: "\(aeroSpaceAppVersion) \(gitHash)") case "--config-path": if let arg = args.getOrNil(atIndex: index) { _serverArgs.configLocation = arg } else { exit(1, err: "Missing in --config-path flag") } index += 1 case "--read-only": // todo rename to '--disabled' and unite with disabled feature _serverArgs.isReadOnly = true case "-NSDocumentRevisionsDebugMode" where isDebug: // Skip Xcode CLI args. // Usually it's '-NSDocumentRevisionsDebugMode NO'/'-NSDocumentRevisionsDebugMode YES' while args.getOrNil(atIndex: index)?.starts(with: "-") == false { index += 1 } default: exit(1, err: "Unrecognized flag '\(args.first.orDie())'") } } if let path = serverArgs.configLocation, !FileManager.default.fileExists(atPath: path) { exit(1, err: "\(path) doesn't exist") } } ================================================ FILE: Sources/AppBundle/layout/layoutRecursive.swift ================================================ import AppKit extension Workspace { @MainActor func layoutWorkspace() async throws { if isEffectivelyEmpty { return } let rect = workspaceMonitor.visibleRectPaddedByOuterGaps // If monitors are aligned vertically and the monitor below has smaller width, then macOS may not allow the // window on the upper monitor to take full width. rect.height - 1 resolves this problem // But I also faced this problem in monitors horizontal configuration. ¯\_(ツ)_/¯ try await layoutRecursive(rect.topLeftCorner, width: rect.width, height: rect.height - 1, virtual: rect, LayoutContext(self)) } } extension TreeNode { @MainActor fileprivate func layoutRecursive(_ point: CGPoint, width: CGFloat, height: CGFloat, virtual: Rect, _ context: LayoutContext) async throws { let physicalRect = Rect(topLeftX: point.x, topLeftY: point.y, width: width, height: height) switch nodeCases { case .workspace(let workspace): lastAppliedLayoutPhysicalRect = physicalRect lastAppliedLayoutVirtualRect = virtual try await workspace.rootTilingContainer.layoutRecursive(point, width: width, height: height, virtual: virtual, context) for window in workspace.children.filterIsInstance(of: Window.self) { window.lastAppliedLayoutPhysicalRect = nil window.lastAppliedLayoutVirtualRect = nil try await window.layoutFloatingWindow(context) } case .window(let window): if window.windowId != currentlyManipulatedWithMouseWindowId { lastAppliedLayoutVirtualRect = virtual if window.isFullscreen && window == context.workspace.rootTilingContainer.mostRecentWindowRecursive { lastAppliedLayoutPhysicalRect = nil window.layoutFullscreen(context) } else { lastAppliedLayoutPhysicalRect = physicalRect window.isFullscreen = false window.setAxFrame(point, CGSize(width: width, height: height)) } } case .tilingContainer(let container): lastAppliedLayoutPhysicalRect = physicalRect lastAppliedLayoutVirtualRect = virtual switch container.layout { case .tiles: try await container.layoutTiles(point, width: width, height: height, virtual: virtual, context) case .accordion: try await container.layoutAccordion(point, width: width, height: height, virtual: virtual, context) } case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer: return // Nothing to do for weirdos } } } private struct LayoutContext { let workspace: Workspace let resolvedGaps: ResolvedGaps @MainActor init(_ workspace: Workspace) { self.workspace = workspace self.resolvedGaps = ResolvedGaps(gaps: config.gaps, monitor: workspace.workspaceMonitor) } } extension Window { @MainActor fileprivate func layoutFloatingWindow(_ context: LayoutContext) async throws { let workspace = context.workspace let windowRect = try await getAxRect() // Probably not idempotent let currentMonitor = windowRect?.center.monitorApproximation if let currentMonitor, let windowRect, workspace != currentMonitor.activeWorkspace { let windowTopLeftCorner = windowRect.topLeftCorner let xProportion = (windowTopLeftCorner.x - currentMonitor.visibleRect.topLeftX) / currentMonitor.visibleRect.width let yProportion = (windowTopLeftCorner.y - currentMonitor.visibleRect.topLeftY) / currentMonitor.visibleRect.height let workspaceRect = workspace.workspaceMonitor.visibleRect var newX = workspaceRect.topLeftX + xProportion * workspaceRect.width var newY = workspaceRect.topLeftY + yProportion * workspaceRect.height let windowWidth = windowRect.width let windowHeight = windowRect.height newX = newX.coerce(in: workspaceRect.minX ... max(workspaceRect.minX, workspaceRect.maxX - windowWidth)) newY = newY.coerce(in: workspaceRect.minY ... max(workspaceRect.minY, workspaceRect.maxY - windowHeight)) setAxFrame(CGPoint(x: newX, y: newY), nil) } if isFullscreen { layoutFullscreen(context) isFullscreen = false } } @MainActor fileprivate func layoutFullscreen(_ context: LayoutContext) { let monitorRect = noOuterGapsInFullscreen ? context.workspace.workspaceMonitor.visibleRect : context.workspace.workspaceMonitor.visibleRectPaddedByOuterGaps setAxFrame(monitorRect.topLeftCorner, CGSize(width: monitorRect.width, height: monitorRect.height)) } } extension TilingContainer { @MainActor fileprivate func layoutTiles(_ point: CGPoint, width: CGFloat, height: CGFloat, virtual: Rect, _ context: LayoutContext) async throws { var point = point var virtualPoint = virtual.topLeftCorner guard let delta = ((orientation == .h ? width : height) - CGFloat(children.sumOfDouble { $0.getWeight(orientation) })) .div(children.count) else { return } let lastIndex = children.indices.last for (i, child) in children.enumerated() { child.setWeight(orientation, child.getWeight(orientation) + delta) let rawGap = context.resolvedGaps.inner.get(orientation).toDouble() // Gaps. Consider 4 cases: // 1. Multiple children. Layout first child // 2. Multiple children. Layout last child // 3. Multiple children. Layout child in the middle // 4. Single child let rawGap = gaps.inner.get(orientation).toDouble() let gap = rawGap - (i == 0 ? rawGap / 2 : 0) - (i == lastIndex ? rawGap / 2 : 0) try await child.layoutRecursive( i == 0 ? point : point.addingOffset(orientation, rawGap / 2), width: orientation == .h ? child.hWeight - gap : width, height: orientation == .v ? child.vWeight - gap : height, virtual: Rect( topLeftX: virtualPoint.x, topLeftY: virtualPoint.y, width: orientation == .h ? child.hWeight : width, height: orientation == .v ? child.vWeight : height, ), context, ) virtualPoint = orientation == .h ? virtualPoint.addingXOffset(child.hWeight) : virtualPoint.addingYOffset(child.vWeight) point = orientation == .h ? point.addingXOffset(child.hWeight) : point.addingYOffset(child.vWeight) } } @MainActor fileprivate func layoutAccordion(_ point: CGPoint, width: CGFloat, height: CGFloat, virtual: Rect, _ context: LayoutContext) async throws { guard let mruIndex: Int = mostRecentChild?.ownIndex else { return } for (index, child) in children.enumerated() { let padding = CGFloat(config.accordionPadding) let (lPadding, rPadding): (CGFloat, CGFloat) = switch index { case 0 where children.count == 1: (0, 0) case 0: (0, padding) case children.indices.last: (padding, 0) case mruIndex - 1: (0, 2 * padding) case mruIndex + 1: (2 * padding, 0) default: (padding, padding) } switch orientation { case .h: try await child.layoutRecursive( point + CGPoint(x: lPadding, y: 0), width: width - rPadding - lPadding, height: height, virtual: virtual, context, ) case .v: try await child.layoutRecursive( point + CGPoint(x: 0, y: lPadding), width: width, height: height - lPadding - rPadding, virtual: virtual, context, ) } } } } ================================================ FILE: Sources/AppBundle/layout/refresh.swift ================================================ import AppKit import Common @MainActor private var activeRefreshTask: Task<(), any Error>? = nil @MainActor func scheduleRefreshSession( _ event: RefreshSessionEvent, optimisticallyPreLayoutWorkspaces: Bool = false, ) { activeRefreshTask?.cancel() activeRefreshTask = Task { @MainActor in try checkCancellation() try await runRefreshSessionBlocking(event, optimisticallyPreLayoutWorkspaces: optimisticallyPreLayoutWorkspaces) } } @MainActor func runRefreshSessionBlocking( _ event: RefreshSessionEvent, layoutWorkspaces shouldLayoutWorkspaces: Bool = true, optimisticallyPreLayoutWorkspaces: Bool = false, ) async throws { let state = signposter.beginInterval(#function, "event: \(event) axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken?.idForDebug)") defer { signposter.endInterval(#function, state) } if !TrayMenuModel.shared.isEnabled { return } try await $refreshSessionEvent.withValue(event) { try await $_isStartup.withValue(event.isStartup) { let nativeFocused = try await getNativeFocusedWindow() if let nativeFocused { try await debugWindowsIfRecording(nativeFocused) } updateFocusCache(nativeFocused) if shouldLayoutWorkspaces && optimisticallyPreLayoutWorkspaces { try await layoutWorkspaces() } refreshModel() try await refresh() gcMonitors() updateTrayText() SecureInputPanel.shared.refresh() try await normalizeLayoutReason() if shouldLayoutWorkspaces { try await layoutWorkspaces() } } } } @MainActor func runLightSession( _ event: RefreshSessionEvent, _: RunSessionGuard, body: @MainActor () async throws -> T, ) async throws -> T { let state = signposter.beginInterval(#function, "event: \(event) axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken?.idForDebug)") defer { signposter.endInterval(#function, state) } activeRefreshTask?.cancel() // Give priority to runSession activeRefreshTask = nil return try await $refreshSessionEvent.withValue(event) { try await $_isStartup.withValue(event.isStartup) { let nativeFocused = try await getNativeFocusedWindow() if let nativeFocused { try await debugWindowsIfRecording(nativeFocused) } updateFocusCache(nativeFocused) let focusBefore = focus.windowOrNil refreshModel() let result = try await body() refreshModel() let focusAfter = focus.windowOrNil updateTrayText() SecureInputPanel.shared.refresh() try await layoutWorkspaces() if focusBefore != focusAfter { focusAfter?.nativeFocus() // syncFocusToMacOs } scheduleRefreshSession(event) return result } } } struct RunSessionGuard: Sendable { @MainActor static var isServerEnabled: RunSessionGuard? { TrayMenuModel.shared.isEnabled ? forceRun : nil } @MainActor static func isServerEnabled(orIsEnableCommand command: (any Command)?) -> RunSessionGuard? { command is EnableCommand ? .forceRun : .isServerEnabled } @MainActor static func checkServerIsEnabledOrDie( file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) -> RunSessionGuard { .isServerEnabled ?? dieT("server is disabled", file: file, line: line, column: column, function: function) } static let forceRun = RunSessionGuard() private init() {} } @MainActor func refreshModel() { Workspace.garbageCollectUnusedWorkspaces() checkOnFocusChangedCallbacks() normalizeContainers() } @MainActor private func refresh() async throws { // Garbage collect terminated apps and windows before working with all windows let mapping = try await MacApp.refreshAllAndGetAliveWindowIds(frontmostAppBundleId: NSWorkspace.shared.frontmostApplication?.bundleIdentifier) let aliveWindowIds = mapping.values.flatMap { $0 }.toSet() for window in MacWindow.allWindows { if !aliveWindowIds.contains(window.windowId) { window.garbageCollect(skipClosedWindowsCache: false) } } for (app, windowIds) in mapping { for windowId in windowIds { try await MacWindow.getOrRegister(windowId: windowId, macApp: app) } } // Garbage collect workspaces after apps, because workspaces contain apps. Workspace.garbageCollectUnusedWorkspaces() } func refreshObs(_: AXObserver, _: AXUIElement, notif: CFString, _: UnsafeMutableRawPointer?) { let notif = notif as String Task { @MainActor in if !TrayMenuModel.shared.isEnabled { return } scheduleRefreshSession(.ax(notif)) } } enum OptimalHideCorner { case bottomLeftCorner, bottomRightCorner } @MainActor private func layoutWorkspaces() async throws { if !TrayMenuModel.shared.isEnabled { for workspace in Workspace.all { workspace.allLeafWindowsRecursive.forEach { ($0 as! MacWindow).unhideFromCorner() } // todo as! try await workspace.layoutWorkspace() // Unhide tiling windows from corner } return } let monitors = monitors var monitorToOptimalHideCorner: [CGPoint: OptimalHideCorner] = [:] for monitor in monitors { let xOff = monitor.width * 0.1 let yOff = monitor.height * 0.1 // brc = bottomRightCorner let brc1 = monitor.rect.bottomRightCorner + CGPoint(x: 2, y: -yOff) let brc2 = monitor.rect.bottomRightCorner + CGPoint(x: -xOff, y: 2) let brc3 = monitor.rect.bottomRightCorner + CGPoint(x: 2, y: 2) // blc = bottomLeftCorner let blc1 = monitor.rect.bottomLeftCorner + CGPoint(x: -2, y: -yOff) let blc2 = monitor.rect.bottomLeftCorner + CGPoint(x: xOff, y: 2) let blc3 = monitor.rect.bottomLeftCorner + CGPoint(x: -2, y: 2) func contains(_ monitor: Monitor, _ point: CGPoint) -> Int { monitor.rect.contains(point) ? 1 : 0 } let important = 10 let corner: OptimalHideCorner = monitors.sumOfInt { contains($0, blc1) + contains($0, blc2) + important * contains($0, blc3) } < monitors.sumOfInt { contains($0, brc1) + contains($0, brc2) + important * contains($0, brc3) } ? .bottomLeftCorner : .bottomRightCorner monitorToOptimalHideCorner[monitor.rect.topLeftCorner] = corner } // to reduce flicker, first unhide visible workspaces, then hide invisible ones for monitor in monitors { let workspace = monitor.activeWorkspace workspace.allLeafWindowsRecursive.forEach { ($0 as! MacWindow).unhideFromCorner() } // todo as! try await workspace.layoutWorkspace() } for workspace in Workspace.all where !workspace.isVisible { let corner = monitorToOptimalHideCorner[workspace.workspaceMonitor.rect.topLeftCorner] ?? .bottomRightCorner for window in workspace.allLeafWindowsRecursive { try await (window as! MacWindow).hideInCorner(corner) // todo as! } } } @MainActor private func normalizeContainers() { // Can't do it only for visible workspace because most of the commands support --window-id and --workspace flags for workspace in Workspace.all { workspace.normalizeContainers() } } ================================================ FILE: Sources/AppBundle/model/AxUiElementWindowType.swift ================================================ import AppKit enum AxUiElementWindowType: String { case window case dialog /// Not even a real window case popup static func new(isWindow: Bool, isDialog: () -> Bool) -> AxUiElementWindowType { switch true { case !isWindow: .popup case isDialog(): .dialog default: .window } } } // Covered by tests in ./axDumps in the repo root extension AxUiElementMock { // 'isDialogHeuristic' function name is referenced in the guide func isDialogHeuristic( _ id: KnownBundleId?, _ windowLevel: MacOsWindowLevel?, ) -> Bool { // Note: a lot of windows don't have title on startup. So please don't rely on the title if id == ._1password && windowLevel != .normalWindow { return true } if id == .iphonesimulator { return true } // Don't tile: // - Chrome cmd+f window ("AXUnknown" value) // - login screen (Yes fuck, it's also a window from Apple's API perspective) ("AXUnknown" value) // - XCode "Build succeeded" popup // - IntelliJ tooltips, context menus, drop downs // - macOS native file picker (IntelliJ -> "Open...") (kAXDialogSubrole value) // // Minimized windows or windows of a hidden app have subrole "AXDialog" if get(Ax.subroleAttr) != kAXStandardWindowSubrole && id != .qutebrowser // qutebrowser regular window has AXDialog subrole when decorations are disabled { return true } // Firefox: Picture in Picture window doesn't have minimize button. // todo. bug: when firefox shows non-native fullscreen, minimize button is disabled for all other non-fullscreen windows if id?.isFirefox == true && get(Ax.minimizeButtonAttr)?.get(Ax.enabledAttr) != true { return true } if id == .photoBooth { return true } if id == .ghostty { return get(Ax.fullscreenButtonAttr)?.get(Ax.enabledAttr) != true && get(Ax.closeButtonAttr)?.get(Ax.enabledAttr) == true } // Heuristic: float windows without fullscreen button (such windows are not designed to be big) // - IntelliJ various dialogs (Rebase..., Edit commit message, Settings, Project structure) // - Finder copy file dialog // - System Settings // - Apple logo -> About this Mac // - Calculator // - Battle.net login dialog // Fullscreen button is presented but disabled: // - Safari -> Pinterest -> Log in with Google // - Kap screen recorder https://github.com/wulkano/Kap // - flameshot? https://github.com/nikitabobko/AeroSpace/issues/112 // - Drata Agent https://github.com/nikitabobko/AeroSpace/issues/134 if get(Ax.fullscreenButtonAttr)?.get(Ax.enabledAttr) != true && id != .gimp && // Gimp doesn't show fullscreen button // "Drag out" a tab out of Chrome window. Technically, it shouldn't be necessary, but // apparently there is some sort of race condition between users releasing mouse up and // Chrome reactivating the fullscreen button // todo: consider checking for fullscreen cirteria periodically (downside: will affect performance) id != .chrome && id != .activityMonitor && // Activity Monitor doesn't show fullscreen button // Terminal apps and Emacs have an option to hide their title bars id != .alacritty && // ~/.alacritty.toml: window.decorations = "Buttonless" id != .kitty && // ~/.config/kitty/kitty.conf: hide_window_decorations titlebar-and-corners id != .wezterm && id != .qutebrowser && // :set window.hide_decoration id != .iterm2 && id != .emacs && id?.isVscode != true && // "window.nativeFullScreen": false id != .steam { return true } return false } /// Alternative name: !isPopup /// /// Why do we need to filter out non-windows? /// - "floating by default" workflow /// - It's annoying that the focus command treats these popups as floating windows func isWindowHeuristic( axApp: AxUiElementMock, _ id: KnownBundleId?, _ activationPolicy: NSApplication.ActivationPolicy, _ windowLevel: MacOsWindowLevel?, ) -> Bool { if windowLevel != .normalWindow && // Slowly roll out windowLevel for applications for which we have the appropriate dumps (id == .slack || id == .chrome || id?.isFirefox == true || id == .braveBrowser || id == .screenstudio || id == .cleanshotx || id == .iterm2) { return false } // Just don't do anything with "Ghostty Quick Terminal" windows. // Its position and size are managed by the Ghostty itself // https://github.com/nikitabobko/AeroSpace/issues/103 // https://github.com/ghostty-org/ghostty/discussions/3512 if id == .ghostty && get(Ax.identifierAttr) == "com.mitchellh.ghostty.quickTerminal" { return false } lazy var fullscreenButton = get(Ax.fullscreenButtonAttr) if id == .xcode && get(Ax.identifierAttr) == "open_quickly" { return false } if id == .iterm2 && fullscreenButton == nil { return false } if activationPolicy == .accessory && get(Ax.closeButtonAttr) == nil && id != .steam { return false } if id?.isFirefox != true { return isWindowHeuristicOld(axApp: axApp, id) } // Try to filter out incredibly weird popup like AXWindows without any buttons. // E.g. // - Sonoma (macOS 14) keyboard layout switch (AXSubrole == AXDialog) // - IntelliJ context menu (right mouse click) // - Telegram context menu (right mouse click) // - Share window purple "pill" indicator https://github.com/nikitabobko/AeroSpace/issues/1101. Title is not empty // - Tooltips on links mouse hover in browsers (Chrome, Firefox) // - Tooltips on buttons (e.g. new tab, Extensions) mouse hover in browsers (Chrome, Firefox). Title is not empty // Make sure that the following AXWindow remain windows: // - macOS native file picker ("Open..." menu) (subrole == kAXDialogSubrole) // - telegram image viewer (subrole == kAXFloatingWindowSubrole) // - Finder preview (hit space) (subrole == "Quick Look") // - Firefox non-native video fullscreen (about:config -> full-screen-api.macos-native-full-screen -> false, subrole == AXUnknown) return get(Ax.closeButtonAttr) != nil || fullscreenButton != nil || get(Ax.zoomButtonAttr) != nil || get(Ax.minimizeButtonAttr) != nil || get(Ax.isFocused) == true || // 3 different ways to detect if the window is focused get(Ax.isMainAttr) == true || axApp.get(Ax.focusedWindowAttr)?.windowId == self.containingWindowId() || get(Ax.subroleAttr) == kAXStandardWindowSubrole } private func isWindowHeuristicOld(axApp: AxUiElementMock, _ id: KnownBundleId?) -> Bool { // 0.18.3 hotfix lazy var subrole = get(Ax.subroleAttr) lazy var title = get(Ax.titleAttr) ?? "" // Try to filter out incredibly weird popup like AXWindows without any buttons. // E.g. // - Sonoma (macOS 14) keyboard layout switch // - IntelliJ context menu (right mouse click) // - Telegram context menu (right mouse click) if get(Ax.closeButtonAttr) == nil && get(Ax.fullscreenButtonAttr) == nil && get(Ax.zoomButtonAttr) == nil && get(Ax.minimizeButtonAttr) == nil && get(Ax.isFocused) == false && // Three different ways to detect if the window is not focused get(Ax.isMainAttr) == false && axApp.get(Ax.focusedWindowAttr)?.windowId != containingWindowId() && subrole != kAXStandardWindowSubrole && // Share window purple "pill" indicator has "Window" title https://github.com/nikitabobko/AeroSpace/issues/1101 (title.isEmpty || title == "Window") // Maybe it doesn't work in non-English locale { return false } return subrole == kAXStandardWindowSubrole || subrole == kAXDialogSubrole || // macOS native file picker ("Open..." menu) (kAXDialogSubrole value) subrole == kAXFloatingWindowSubrole || // telegram image viewer id == .finder && subrole == "Quick Look" // Finder preview (hit space) is a floating window } func getWindowType( axApp: AxUiElementMock, _ id: KnownBundleId?, _ activationPolicy: NSApplication.ActivationPolicy, _ windowLevel: MacOsWindowLevel?, ) -> AxUiElementWindowType { .new( isWindow: isWindowHeuristic(axApp: axApp, id, activationPolicy, windowLevel), isDialog: { isDialogHeuristic(id, windowLevel) }, ) } } ================================================ FILE: Sources/AppBundle/model/Json.swift ================================================ import AppKit import Common enum Json: Encodable, Equatable { // vector case dict([String: Json]) case array([Json]) // scalar case null case string(String) case int(Int) case uint32(UInt32) case bool(Bool) func encode(to encoder: any Encoder) throws { switch self { case .array(let value): try value.encode(to: encoder) case .dict(let value): try value.encode(to: encoder) case .string(let value): try value.encode(to: encoder) case .int(let value): try value.encode(to: encoder) case .uint32(let value): try value.encode(to: encoder) case .bool(let value): try value.encode(to: encoder) case .null: try (nil as String?).encode(to: encoder) } } static func newOrDie(_ value: Any?) -> Json { if let value = value as? [String: Any?] { return .dict(value.mapValues(newOrDie)) } else if let value = value as? [Any?] { return .array(value.map(newOrDie)) } else if let value = value as? Int { return .int(value) } else if let value = value as? UInt32 { return .uint32(value) } else if let value = value as? Bool { return .bool(value) } else if let value = value as? String { return .string(value) } else if value == nil || value is NSNull { return .null } else { die("Can't parse \(String(describing: value)) (\(type(of: value))) to JSON") } } static func stringOrNull(_ str: String?) -> Json { str.map(Json.string) ?? .null } var rawValue: Any? { switch self { case .null: nil case .array(let x): x case .dict(let x): x case .bool(let x): x case .int(let x): x case .string(let x): x case .uint32(let x): x } } var asDictOrDie: [String: Json] { if case .dict(let dict) = self { dict } else { dieT("\(self) is not a dict") } } } ================================================ FILE: Sources/AppBundle/model/KnownBundleId.swift ================================================ enum KnownBundleId: String, Equatable { case _1password = "com.1password.1password" case activityMonitor = "com.apple.ActivityMonitor" case alacritty = "org.alacritty" case braveBrowser = "com.brave.Browser" case chrome = "com.google.Chrome" case cleanshotx = "pl.maketheweb.cleanshotx" case emacs = "org.gnu.Emacs" case finder = "com.apple.finder" case ghostty = "com.mitchellh.ghostty" case gimp = "org.gimp.gimp-2.10" case iphonesimulator = "com.apple.iphonesimulator" case iterm2 = "com.googlecode.iterm2" case kitty = "net.kovidgoyal.kitty" case photoBooth = "com.apple.PhotoBooth" case qutebrowser = "org.qutebrowser.qutebrowser" case screenstudio = "com.timpler.screenstudio" case slack = "com.tinyspeck.slackmacgap" case steam = "com.valvesoftware.steam.helper" case wezterm = "com.github.wez.wezterm" case xcode = "com.apple.dt.Xcode" case zenBrowser = "app.zen-browser.zen" case zoom = "us.zoom.xos" case mozillaFirefox = "org.mozilla.firefox" case mozillaFirefoxDeveloperEdition = "org.mozilla.firefoxdeveloperedition" case mozillaFirefoxNightly = "org.mozilla.nightly" case vscode = "com.microsoft.VSCode" case vscodium = "com.vscodium" var isFirefox: Bool { self == .mozillaFirefox || self == .mozillaFirefoxDeveloperEdition || self == .mozillaFirefoxNightly || self == .zenBrowser } var isVscode: Bool { self == .vscode || self == .vscodium } } ================================================ FILE: Sources/AppBundle/model/Monitor.swift ================================================ import AppKit import Common private struct MonitorImpl { let monitorAppKitNsScreenScreensId: Int let name: String let rect: Rect let visibleRect: Rect let isMain: Bool } extension MonitorImpl: Monitor { var height: CGFloat { rect.height } var width: CGFloat { rect.width } } /// Use it instead of NSScreen because it can be mocked in tests protocol Monitor: AeroAny { /// The index in NSScreen.screens array. 1-based index var monitorAppKitNsScreenScreensId: Int { get } var name: String { get } var rect: Rect { get } var visibleRect: Rect { get } var width: CGFloat { get } var height: CGFloat { get } var isMain: Bool { get } } final class LazyMonitor: Monitor { private let screen: NSScreen let monitorAppKitNsScreenScreensId: Int let name: String let width: CGFloat let height: CGFloat let isMain: Bool private var _rect: Rect? private var _visibleRect: Rect? init(monitorAppKitNsScreenScreensId: Int, isMain: Bool, _ screen: NSScreen) { self.monitorAppKitNsScreenScreensId = monitorAppKitNsScreenScreensId self.name = screen.localizedName self.width = screen.frame.width // Don't call rect because it would cause recursion during mainMonitor init self.height = screen.frame.height // Don't call rect because it would cause recursion during mainMonitor init self.screen = screen self.isMain = isMain } var rect: Rect { _rect ?? screen.rect.also { _rect = $0 } } var visibleRect: Rect { _visibleRect ?? screen.visibleRect.also { _visibleRect = $0 } } } // Note to myself: Don't use NSScreen.main, it's garbage // 1. The name is misleading, it's supposed to be called "focusedScreen" // 2. It's inaccurate because NSScreen.main doesn't work correctly from NSWorkspace.didActivateApplicationNotification & // kAXFocusedWindowChangedNotification callbacks. extension NSScreen { fileprivate func toMonitor(monitorAppKitNsScreenScreensId: Int) -> Monitor { MonitorImpl( monitorAppKitNsScreenScreensId: monitorAppKitNsScreenScreensId, name: localizedName, rect: rect, visibleRect: visibleRect, isMain: isMainScreen, ) } fileprivate var isMainScreen: Bool { frame.minX == 0 && frame.minY == 0 } /// The property is a replacement for Apple's crazy ``frame`` /// /// - For ``MacWindow.topLeftCorner``, (0, 0) is main screen top left corner, and positive y-axis goes down. /// - For ``frame``, (0, 0) is main screen bottom left corner, and positive y-axis goes up (which is crazy). /// /// The property "normalizes" ``frame`` fileprivate var rect: Rect { frame.monitorFrameNormalized() } /// Same as ``rect`` but for ``visibleFrame`` fileprivate var visibleRect: Rect { visibleFrame.monitorFrameNormalized() } } private let testMonitorRect = Rect(topLeftX: 0, topLeftY: 0, width: 1920, height: 1080) private let testMonitor = MonitorImpl( monitorAppKitNsScreenScreensId: 1, name: "Test Monitor", rect: testMonitorRect, visibleRect: testMonitorRect, isMain: true, ) var mainMonitor: Monitor { if isUnitTest { return testMonitor } let screens = NSScreen.screens // Fallback: If main screen can't be found (e.g., during display reconfiguration), // return screens.first or testMonitor to avoid crash let screen = screens.withIndex.singleOrNil(where: \.value.isMainScreen) ?? screens.first.map { (0, $0) } guard let screen else { return testMonitor } return LazyMonitor(monitorAppKitNsScreenScreensId: screen.index + 1, isMain: true, screen.value) } var monitors: [Monitor] { isUnitTest ? [testMonitor] : NSScreen.screens.enumerated().map { $0.element.toMonitor(monitorAppKitNsScreenScreensId: $0.offset + 1) } } var sortedMonitors: [Monitor] { monitors.sortedBy([\.rect.minX, \.rect.minY]) } ================================================ FILE: Sources/AppBundle/model/MonitorDescriptionEx.swift ================================================ import Common extension MonitorDescription { func resolveMonitor(sortedMonitors: [Monitor]) -> Monitor? { return switch self { case .sequenceNumber(let number): sortedMonitors.getOrNil(atIndex: number - 1) case .main: mainMonitor case .pattern(_, let regex): sortedMonitors.first { monitor in monitor.name.contains(regex.val) } case .secondary: sortedMonitors.takeIf { $0.count == 2 }? .first { $0.rect.topLeftCorner != mainMonitor.rect.topLeftCorner } } } } ================================================ FILE: Sources/AppBundle/model/MonitorEx.swift ================================================ extension Monitor { @MainActor var visibleRectPaddedByOuterGaps: Rect { let topLeft = visibleRect.topLeftCorner let gaps = ResolvedGaps(gaps: config.gaps, monitor: self) return Rect( topLeftX: topLeft.x + gaps.outer.left.toDouble(), topLeftY: topLeft.y + gaps.outer.top.toDouble(), width: visibleRect.width - gaps.outer.left.toDouble() - gaps.outer.right.toDouble(), height: visibleRect.height - gaps.outer.top.toDouble() - gaps.outer.bottom.toDouble(), ) } var monitorId_oneBased: Int? { let sorted = sortedMonitors let origin = self.rect.topLeftCorner return sorted.firstIndex { $0.rect.topLeftCorner == origin }.map { $0 + 1 } } } ================================================ FILE: Sources/AppBundle/model/Rect.swift ================================================ import AppKit import Common struct Rect: ConvenienceCopyable, AeroAny { var topLeftX: CGFloat var topLeftY: CGFloat private var _width: CGFloat var width: CGFloat { get { max(_width, 0) } set(newValue) { _width = newValue } } private var _height: CGFloat var height: CGFloat { get { max(_height, 0) } set(newValue) { _height = newValue } } init(topLeftX: CGFloat, topLeftY: CGFloat, width: CGFloat, height: CGFloat) { self.topLeftX = topLeftX self.topLeftY = topLeftY self._width = width self._height = height } } extension CGRect { func monitorFrameNormalized() -> Rect { let mainMonitorHeight: CGFloat = mainMonitor.height let rect = toRect() return rect.copy(\.topLeftY, mainMonitorHeight - rect.topLeftY) } } extension CGRect { func toRect() -> Rect { Rect(topLeftX: minX, topLeftY: maxY, width: width, height: height) } } extension Rect { func contains(_ point: CGPoint) -> Bool { minX.until(excl: maxX)?.contains(point.x) == true && minY.until(excl: maxY)?.contains(point.y) == true } var center: CGPoint { CGPoint(x: topLeftX + width / 2, y: topLeftY + height / 2) } var topLeftCorner: CGPoint { CGPoint(x: topLeftX, y: topLeftY) } var topRightCorner: CGPoint { CGPoint(x: maxX, y: minY) } var bottomRightCorner: CGPoint { CGPoint(x: maxX, y: maxY) } var bottomLeftCorner: CGPoint { CGPoint(x: minX, y: maxY) } var minY: CGFloat { topLeftY } var maxY: CGFloat { topLeftY + height } var minX: CGFloat { topLeftX } var maxX: CGFloat { topLeftX + width } var size: CGSize { CGSize(width: width, height: height) } func getDimension(_ orientation: Orientation) -> CGFloat { orientation == .h ? width : height } } ================================================ FILE: Sources/AppBundle/model/ServerEvent.swift ================================================ import Common public struct ServerEvent: Codable, Sendable { private let _event: ServerEventType // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var windowId: UInt32? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var workspace: String? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var prevWorkspace: String? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var monitorId: Int? // 1-based // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var appBundleId: String? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var appName: String? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var mode: String? // periphery:ignore - false positive unused warning. The var properties are serialized to JSON private var binding: String? public var eventType: ServerEventType { _event } public static func focusChanged(windowId: UInt32?, workspace: String) -> ServerEvent { ServerEvent(_event: .focusChanged, windowId: windowId, workspace: workspace) } public static func focusedMonitorChanged(workspace: String, monitorId_oneBased: Int) -> ServerEvent { ServerEvent(_event: .focusedMonitorChanged, workspace: workspace, monitorId: monitorId_oneBased) } public static func workspaceChanged(workspace: String, prevWorkspace: String) -> ServerEvent { ServerEvent(_event: .workspaceChanged, workspace: workspace, prevWorkspace: prevWorkspace) } public static func modeChanged(mode: String?) -> ServerEvent { ServerEvent(_event: .modeChanged, mode: mode) } public static func windowDetected(windowId: UInt32, workspace: String?, appBundleId: String?, appName: String?) -> ServerEvent { ServerEvent(_event: .windowDetected, windowId: windowId, workspace: workspace, appBundleId: appBundleId, appName: appName) } public static func bindingTriggered(mode: String, binding: String) -> ServerEvent { ServerEvent(_event: .bindingTriggered, mode: mode, binding: binding) } } ================================================ FILE: Sources/AppBundle/mouse/mouse.swift ================================================ import AppKit @MainActor var currentlyManipulatedWithMouseWindowId: UInt32? = nil var isLeftMouseButtonDown: Bool { NSEvent.pressedMouseButtons == 1 } @MainActor func isManipulatedWithMouse(_ window: Window) async throws -> Bool { try await (!window.isHiddenInCorner && // Don't allow to resize/move windows of hidden workspaces isLeftMouseButtonDown && (currentlyManipulatedWithMouseWindowId == nil || window.windowId == currentlyManipulatedWithMouseWindowId)) .andAsync { @Sendable @MainActor in try await getNativeFocusedWindow() == window } } /// Same motivation as in monitorFrameNormalized var mouseLocation: CGPoint { let mainMonitorHeight: CGFloat = mainMonitor.height let location = NSEvent.mouseLocation return location.copy(\.y, mainMonitorHeight - location.y) } ================================================ FILE: Sources/AppBundle/mouse/moveWithMouse.swift ================================================ import AppKit import Common @MainActor private var moveWithMouseTask: Task<(), any Error>? = nil func movedObs(_: AXObserver, ax: AXUIElement, notif: CFString, _: UnsafeMutableRawPointer?) { let windowId = ax.containingWindowId() let notif = notif as String Task { @MainActor in guard let token: RunSessionGuard = .isServerEnabled else { return } guard let windowId, let window = Window.get(byId: windowId), try await isManipulatedWithMouse(window) else { scheduleRefreshSession(.ax(notif)) return } moveWithMouseTask?.cancel() moveWithMouseTask = Task { try checkCancellation() try await runLightSession(.ax(notif), token) { try await moveWithMouse(window) } } } } @MainActor private func moveWithMouse(_ window: Window) async throws { // todo cover with tests resetClosedWindowsCache() guard let parent = window.parent else { return } switch parent.cases { case .workspace: try await moveFloatingWindow(window) case .tilingContainer: moveTilingWindow(window) case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer: return // Unconventional windows can't be moved with mouse } } @MainActor private func moveFloatingWindow(_ window: Window) async throws { guard let targetWorkspace = try await window.getCenter()?.monitorApproximation.activeWorkspace else { return } guard let parent = window.parent else { return } if targetWorkspace != parent { window.bindAsFloatingWindow(to: targetWorkspace) } } @MainActor private func moveTilingWindow(_ window: Window) { currentlyManipulatedWithMouseWindowId = window.windowId window.lastAppliedLayoutPhysicalRect = nil let mouseLocation = mouseLocation let targetWorkspace = mouseLocation.monitorApproximation.activeWorkspace let swapTarget = mouseLocation.findIn(tree: targetWorkspace.rootTilingContainer, virtual: false)?.takeIf { $0 != window } if targetWorkspace != window.nodeWorkspace { // Move window to a different monitor let index: Int = if let swapTarget, let parent = swapTarget.parent as? TilingContainer, let targetRect = swapTarget.lastAppliedLayoutPhysicalRect { mouseLocation.getProjection(parent.orientation) >= targetRect.center.getProjection(parent.orientation) ? swapTarget.ownIndex.orDie() + 1 : swapTarget.ownIndex.orDie() } else { 0 } window.bind( to: swapTarget?.parent ?? targetWorkspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: index, ) } else if let swapTarget { swapWindows(window, swapTarget) } } @MainActor func swapWindows(_ window1: Window, _ window2: Window) { if window1 == window2 { return } guard let index1 = window1.ownIndex else { return } guard let index2 = window1.ownIndex else { return } if index1 < index2 { let binding2 = window2.unbindFromParent() let binding1 = window1.unbindFromParent() window2.bind(to: binding1.parent, adaptiveWeight: binding1.adaptiveWeight, index: binding1.index) window1.bind(to: binding2.parent, adaptiveWeight: binding2.adaptiveWeight, index: binding2.index) } else { let binding1 = window1.unbindFromParent() let binding2 = window2.unbindFromParent() window1.bind(to: binding2.parent, adaptiveWeight: binding2.adaptiveWeight, index: binding2.index) window2.bind(to: binding1.parent, adaptiveWeight: binding1.adaptiveWeight, index: binding1.index) } } extension CGPoint { @MainActor func findIn(tree: TilingContainer, virtual: Bool) -> Window? { let point = self let target: TreeNode? = switch tree.layout { case .tiles: tree.children.first(where: { (virtual ? $0.lastAppliedLayoutVirtualRect : $0.lastAppliedLayoutPhysicalRect)?.contains(point) == true }) case .accordion: tree.mostRecentChild } guard let target else { return nil } return switch target.tilingTreeNodeCasesOrDie() { case .window(let window): window case .tilingContainer(let container): findIn(tree: container, virtual: virtual) } } } ================================================ FILE: Sources/AppBundle/mouse/resizeWithMouse.swift ================================================ import AppKit import Common @MainActor private var resizeWithMouseTask: Task<(), any Error>? = nil func resizedObs(_: AXObserver, ax: AXUIElement, notif: CFString, _: UnsafeMutableRawPointer?) { let notif = notif as String let windowId = ax.containingWindowId() Task { @MainActor in guard let token: RunSessionGuard = .isServerEnabled else { return } guard let windowId, let window = Window.get(byId: windowId), try await isManipulatedWithMouse(window) else { scheduleRefreshSession(.ax(notif)) return } resizeWithMouseTask?.cancel() resizeWithMouseTask = Task { try checkCancellation() try await runLightSession(.ax(notif), token) { try await resizeWithMouse(window) } } } } @MainActor func resetManipulatedWithMouseIfPossible() async throws { if currentlyManipulatedWithMouseWindowId != nil { currentlyManipulatedWithMouseWindowId = nil for workspace in Workspace.all { workspace.resetResizeWeightBeforeResizeRecursive() } scheduleRefreshSession(.resetManipulatedWithMouse, optimisticallyPreLayoutWorkspaces: true) } } private let adaptiveWeightBeforeResizeWithMouseKey = TreeNodeUserDataKey(key: "adaptiveWeightBeforeResizeWithMouseKey") @MainActor private func resizeWithMouse(_ window: Window) async throws { // todo cover with tests resetClosedWindowsCache() guard let parent = window.parent else { return } switch parent.cases { case .workspace, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosPopupWindowsContainer, .macosHiddenAppsWindowsContainer: return // Nothing to do for floating, or unconventional windows case .tilingContainer: guard let rect = try await window.getAxRect() else { return } guard let lastAppliedLayoutRect = window.lastAppliedLayoutPhysicalRect else { return } let (lParent, lOwnIndex) = window.closestParent(hasChildrenInDirection: .left, withLayout: .tiles) ?? (nil, nil) let (dParent, dOwnIndex) = window.closestParent(hasChildrenInDirection: .down, withLayout: .tiles) ?? (nil, nil) let (uParent, uOwnIndex) = window.closestParent(hasChildrenInDirection: .up, withLayout: .tiles) ?? (nil, nil) let (rParent, rOwnIndex) = window.closestParent(hasChildrenInDirection: .right, withLayout: .tiles) ?? (nil, nil) let table: [(CGFloat, TilingContainer?, Int?, Int?)] = [ (lastAppliedLayoutRect.minX - rect.minX, lParent, 0, lOwnIndex), // Horizontal, to the left of the window (rect.maxY - lastAppliedLayoutRect.maxY, dParent, dOwnIndex.map { $0 + 1 }, dParent?.children.count), // Vertical, to the down of the window (lastAppliedLayoutRect.minY - rect.minY, uParent, 0, uOwnIndex), // Vertical, to the up of the window (rect.maxX - lastAppliedLayoutRect.maxX, rParent, rOwnIndex.map { $0 + 1 }, rParent?.children.count), // Horizontal, to the right of the window ] for (diff, parent, startIndex, pastTheEndIndex) in table { if let parent, let startIndex, let pastTheEndIndex, pastTheEndIndex - startIndex > 0 && abs(diff) > 5 { // 5 pixels should be enough to fight with accumulated floating precision error let siblingDiff = diff.div(pastTheEndIndex - startIndex).orDie() let orientation = parent.orientation window.parentsWithSelf.lazy .prefix(while: { $0 != parent }) .filter { let parent = $0.parent as? TilingContainer return parent?.orientation == orientation && parent?.layout == .tiles } .forEach { $0.setWeight(orientation, $0.getWeightBeforeResize(orientation) + diff) } for sibling in parent.children[startIndex ..< pastTheEndIndex] { sibling.setWeight(orientation, sibling.getWeightBeforeResize(orientation) - siblingDiff) } } } currentlyManipulatedWithMouseWindowId = window.windowId } } extension TreeNode { @MainActor fileprivate func getWeightBeforeResize(_ orientation: Orientation) -> CGFloat { let currentWeight = getWeight(orientation) // Check assertions return getUserData(key: adaptiveWeightBeforeResizeWithMouseKey) ?? (lastAppliedLayoutVirtualRect?.getDimension(orientation) ?? currentWeight) .also { putUserData(key: adaptiveWeightBeforeResizeWithMouseKey, data: $0) } } fileprivate func resetResizeWeightBeforeResizeRecursive() { cleanUserData(key: adaptiveWeightBeforeResizeWithMouseKey) for child in children { child.resetResizeWeightBeforeResizeRecursive() } } } ================================================ FILE: Sources/AppBundle/normalizeLayoutReason.swift ================================================ @MainActor func normalizeLayoutReason() async throws { for workspace in Workspace.all { let windows: [Window] = workspace.allLeafWindowsRecursive try await _normalizeLayoutReason(workspace: workspace, windows: windows) } try await _normalizeLayoutReason(workspace: focus.workspace, windows: macosMinimizedWindowsContainer.children.filterIsInstance(of: Window.self)) try await validateStillPopups() } @MainActor private func validateStillPopups() async throws { for node in macosPopupWindowsContainer.children { let popup = (node as! MacWindow) let windowLevel = getWindowLevel(for: popup.windowId) if try await popup.isWindowHeuristic(windowLevel) { try await popup.relayoutWindow(on: focus.workspace) try await tryOnWindowDetected(popup) } } } @MainActor private func _normalizeLayoutReason(workspace: Workspace, windows: [Window]) async throws { for window in windows { let isMacosFullscreen = try await window.isMacosFullscreen let isMacosMinimized = try await (!isMacosFullscreen).andAsync { @MainActor @Sendable in try await window.isMacosMinimized } let isMacosWindowOfHiddenApp = !isMacosFullscreen && !isMacosMinimized && !config.automaticallyUnhideMacosHiddenApps && window.macAppUnsafe.nsApp.isHidden switch window.layoutReason { case .standard: guard let parent = window.parent else { continue } switch true { case isMacosFullscreen: window.layoutReason = .macos(prevParentKind: parent.kind) window.bind(to: workspace.macOsNativeFullscreenWindowsContainer, adaptiveWeight: WEIGHT_DOESNT_MATTER, index: INDEX_BIND_LAST) case isMacosMinimized: window.layoutReason = .macos(prevParentKind: parent.kind) window.bind(to: macosMinimizedWindowsContainer, adaptiveWeight: 1, index: INDEX_BIND_LAST) case isMacosWindowOfHiddenApp: window.layoutReason = .macos(prevParentKind: parent.kind) window.bind(to: workspace.macOsNativeHiddenAppsWindowsContainer, adaptiveWeight: WEIGHT_DOESNT_MATTER, index: INDEX_BIND_LAST) default: break } case .macos(let prevParentKind): if !isMacosFullscreen && !isMacosMinimized && !isMacosWindowOfHiddenApp { try await exitMacOsNativeUnconventionalState(window: window, prevParentKind: prevParentKind, workspace: workspace) } } } } @MainActor func exitMacOsNativeUnconventionalState(window: Window, prevParentKind: NonLeafTreeNodeKind, workspace: Workspace) async throws { window.layoutReason = .standard switch prevParentKind { case .workspace: window.bindAsFloatingWindow(to: workspace) case .tilingContainer: try await window.relayoutWindow(on: workspace, forceTile: true) case .macosPopupWindowsContainer: // Since the window was minimized/fullscreened it was mistakenly detected as popup. Relayout the window try await window.relayoutWindow(on: workspace) case .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer: // wtf case, should never be possible. But If encounter it, let's just re-layout window try await window.relayoutWindow(on: workspace) } } ================================================ FILE: Sources/AppBundle/runLoop.swift ================================================ import Common import Foundation extension Thread { @discardableResult func runInLoopAsync( job: RunLoopJob = RunLoopJob(), autoCheckCancelled: Bool = true, _ body: @Sendable @escaping (RunLoopJob) -> (), ) -> RunLoopJob { let action = RunLoopAction(job: job, autoCheckCancelled: autoCheckCancelled, body) // Alternative: CFRunLoopPerformBlock + CFRunLoopWakeUp action.perform(#selector(action.action), on: self, with: nil, waitUntilDone: false) return job } func runInLoop(_ body: @Sendable @escaping (RunLoopJob) throws -> T) async throws -> T { // todo try to convert to typed throws try checkCancellation() let job = RunLoopJob() return try await withTaskCancellationHandler { try await withCheckedThrowingContinuation { cont in // It's unsafe to implicitly cancel because cont.resume should be invoked exactly once self.runInLoopAsync(job: job, autoCheckCancelled: false) { job in do { try job.checkCancellation() cont.resume(returning: try body(job)) } catch { cont.resume(throwing: error) } } } } onCancel: { job.cancel() } } } private final class RunLoopAction: NSObject, Sendable { private let _action: @Sendable (RunLoopJob) -> () let job: RunLoopJob private let autoCheckCancelled: Bool private let _refreshSessionEvent: RefreshSessionEvent? init(job: RunLoopJob, autoCheckCancelled: Bool, _ action: @escaping @Sendable (RunLoopJob) -> ()) { self.job = job self.autoCheckCancelled = autoCheckCancelled _action = action _refreshSessionEvent = refreshSessionEvent } @objc func action() { if autoCheckCancelled && job.isCancelled { return } $refreshSessionEvent.withValue(_refreshSessionEvent) { _action(job) } } } final class RunLoopJob: Sendable, AeroAny { // Alternative 1. In macOS 15, it's possible to use `Atomic` from `Synchronization` module // Alternative 2. https://github.com/apple/swift-atomics/tree/main but I don't want to add one more dependency just for // AtomicBool nonisolated(unsafe) private var _isCancelled: Int32 = 0 var isCancelled: Bool { _isCancelled == 1 } func cancel() { while !isCancelled { OSAtomicCompareAndSwapInt(0, 1, &_isCancelled) } } static let cancelled: RunLoopJob = RunLoopJob().also { $0.cancel() } func checkCancellation() throws { if isCancelled { throw CancellationError() } } } ================================================ FILE: Sources/AppBundle/server.swift ================================================ import AppKit import Common import Network func startUnixSocketServer() { try? FileManager.default.removeItem(atPath: socketPath) let params = NWParameters.tcp params.requiredLocalEndpoint = .unix(path: socketPath) let listener = Result { try NWListener(using: params) }.getOrDie() listener.newConnectionHandler = { connection in Task { defer { connection.cancel() } connection.start(queue: .global()) await newConnection(connection) } } listener.start(queue: .global()) } func toggleReleaseServerIfDebug(_ state: EnableCmdArgs.State) async { if serverArgs.isReadOnly { return } if !isDebug { return } let socketFile = "/tmp/\(stableAeroSpaceAppId)-\(unixUserName).sock" let connection = NWConnection(to: NWEndpoint.unix(path: socketFile), using: .tcp) defer { connection.cancel() } if await connection.startBlocking().error != nil { // Can't connect, AeroSpace.app is not running return } let req = ClientRequest(args: ["enable", state.rawValue], stdin: "", windowId: nil, workspace: nil) _ = await connection.writeAtomic(req) _ = await connection.readNonAtomic() } private let serverVersionAndHash = "\(aeroSpaceAppVersion) \(gitHash)" private func newConnection(_ connection: NWConnection) async { // todo add exit codes func answerToClient(exitCode: Int32, stdout: String = "", stderr: String = "") async { let ans = ServerAnswer(exitCode: exitCode, stdout: stdout, stderr: stderr, serverVersionAndHash: serverVersionAndHash) await answerToClient(ans) } func answerToClient(_ ans: ServerAnswer) async { _ = await connection.writeAtomic(ans) } while true { let rawRequest: Data switch await connection.readNonAtomic() { case .success(let _rawRequest): rawRequest = _rawRequest case .failure(let error): await answerToClient(exitCode: 1, stderr: "Error: \(error)") return } let request: ClientRequest switch ClientRequest.decodeJson(rawRequest) { case .success(let _request): request = _request case .failure(let error): await answerToClient( exitCode: 1, stderr: """ Can't parse request '\(String(describing: String(data: rawRequest, encoding: .utf8)).singleQuoted)'. Error: \(error) """, ) continue } // Handle subscribe before parseCommand (subscribe doesn't have a Command impl) if request.args.first == "subscribe" { switch parseSubscribeCmdArgs(request.args.slice(1...).orDie()) { case .cmd(let subscribeArgs): await handleSubscribeAndWaitTillError(connection, subscribeArgs) case .help(let help): await answerToClient(exitCode: 0, stdout: help) case .failure(let err): await answerToClient(exitCode: 1, stderr: err) } continue } let (command, help, err) = parseCommand(request.args).unwrap() guard let token: RunSessionGuard = await .isServerEnabled(orIsEnableCommand: command) else { await answerToClient( exitCode: 1, stderr: "\(aeroSpaceAppName) server is disabled and doesn't accept commands. " + "You can use 'aerospace enable on' to enable the server", ) continue } if let help { await answerToClient(exitCode: 0, stdout: help) continue } if let err { await answerToClient(exitCode: 1, stderr: err) continue } if command?.isExec == true { await answerToClient(exitCode: 1, stderr: "exec-and-forget is prohibited in CLI") continue } if let command { let _answer: Result = await Result { try await runLightSession(.socketServer(command.args), token) { () throws in let env = CmdEnv.init( windowId: request.windowId.flatMap { $0 }, workspaceName: request.workspace.flatMap { $0 }, ) let cmdResult = try await command.run(env, CmdStdin(request.stdin)) return ServerAnswer( exitCode: cmdResult.exitCode, stdout: cmdResult.stdout.joined(separator: "\n"), stderr: cmdResult.stderr.joined(separator: "\n"), serverVersionAndHash: serverVersionAndHash, ) } } var answer = _answer.getOrNil() ?? ServerAnswer( exitCode: 1, stderr: "Fail to await main thread. \(_answer.failureOrNil?.localizedDescription ?? "")", serverVersionAndHash: serverVersionAndHash, ) if request.windowId == nil || request.workspace == nil { answer.stderr += "\n\nAeroSpace client has sent incomplete JSON request. 'windowId' or/and 'workspace' fields are missing. Please forward your AEROSPACE_WINDOW_ID and AEROSPACE_WORKSPACE environment variables to these JSON fields. If the appropriate environment variables are empty, pass explict 'null' in the JSON." } await answerToClient(answer) continue } die("Unreachable") } } ================================================ FILE: Sources/AppBundle/shell/Shell.swift ================================================ // periphery:ignore:all - This is WIP file todo import Antlr4 import Common import ShellParserGenerated typealias TK = ShellParser.Tokens /// Use the following technique for quick grammar testing: /// source .deps/python-venv/bin/activate.fish /// echo "foo bar" | antlr4-parse ./grammar/ShellLexer.g4 ./grammar/ShellParser.g4 root -gui extension String { func parseShell() -> Result { let stream = ANTLRInputStream(self) let lexer = ShellLexer(stream) let errorsCollector = ErrorListenerCollector() lexer.addErrorListener(errorsCollector) let tokenStream = CommonTokenStream(lexer) let parser: ShellParser switch Result(catching: { try ShellParser(tokenStream) }) { case .success(let x): parser = x case .failure(let msg): return .failure(msg.localizedDescription) } parser.addErrorListener(errorsCollector) let root: ShellParser.RootContext switch Result(catching: { try parser.root() }) { case .success(let x): root = x case .failure(let msg): return .failure(msg.localizedDescription) } if !errorsCollector.errors.isEmpty { return .failure(errorsCollector.errors.joinErrors()) } return root.cmds().map { $0.toTyped() } ?? .success(.empty) } } let keywords = [TK.DO, TK.THEN, TK.IF, TK.END, TK.ELSE, TK.SWITCH, TK.IN, TK.CASE, TK.WHILE, TK.DEFER, TK.FOR, TK.CATCH].map(\.rawValue) final class ErrorListenerCollector: BaseErrorListener { var errors: [String] = [] override func syntaxError( _ recognizer: Recognizer, _ offendingSymbol: AnyObject?, _ line: Int, _ charPositionInLine: Int, _ msg: String, _ e: AnyObject?, ) { let offendingToken = (offendingSymbol as? Token)?.getType() if offendingToken == TK.TRIPLE_QUOTE.rawValue { errors.append("Triple quotes are reserved for future use. Please put spaces in between if you meant separate args") return } let helper = if let offendingToken, keywords.contains(offendingToken), let name = ShellParser.VOCABULARY.getSymbolicName(offendingToken) { ". \(name) is a reserved keyword. Please, put quotes around it, if you want to use it as an argument" } else if offendingToken == TK.ANY.rawValue { ". Please put the character/word in quotes, if you want to use it as an argument" } else { "" } errors.append("Syntax error at \(line):\(charPositionInLine) \(msg)\(helper)") } } extension ShellParser.CmdsContext { func toTyped() -> Result { if let x = self as? ShellParser.SeqContext { return x.cmd().toTyped("seq node: nil cmd") .combine { x.cmds().mapAllOrFailures { $0.toTyped() }.mapError { $0.joinErrors() } } .flatMap { let seq = [$0.0] + $0.1 return switch seq.count { case 0: .failure("seq node: 0 children") case 1: .success(seq.first.orDie()) default: .success(Shell.seq(seq)) } } } if let x = self as? ShellParser.IfElseContext { return Result { try parseIfElse(x) }.mapError { $0 as! String } } return .failure("Unknown node type: \(self)") } } private func parseIfElse(_ ifElse: ShellParser.IfElseContext) throws -> Shell { var lastCond: Shell? = nil var branches: [Branch] = [] var elseVisited = false guard let children = ifElse.children else { throw "switch if: nil children" } for child in children { if let child = child as? ShellParser.CmdContext { if let lastCond { branches.append(Branch(cond: lastCond, then: nil)) } lastCond = try child.toTyped().get() } else if let child = child as? ShellParser.CmdsContext { if elseVisited { return .ifElse(IfElse(branches: branches, elseBranch: try child.toTyped().get())) } else { try branches.append(Branch(cond: lastCond ?? throwT("nil lastCond"), then: child.toTyped().get())) lastCond = nil } } else if let child = child as? TerminalNode, child.getSymbol()?.getType() == TK.ELSE.rawValue { elseVisited = true if let lastCond { branches.append(Branch(cond: lastCond, then: nil)) } lastCond = nil } } if let lastCond { if elseVisited { throw "ifElse node: wtf" } return .ifElse(IfElse(branches: [Branch(cond: lastCond, then: nil)], elseBranch: nil)) } return .ifElse(IfElse(branches: branches, elseBranch: nil)) } extension ShellParser.CmdContext { func toTyped() -> Result { if let x = self as? ShellParser.PipeContext { return binaryNode(Shell.pipe, x.cmd(0), x.cmd(1)) } if let x = self as? ShellParser.AndContext { return binaryNode(Shell.and, x.cmd(0), x.cmd(1)) } if let x = self as? ShellParser.OrContext { return binaryNode(Shell.or, x.cmd(0), x.cmd(1)) } if let x = self as? ShellParser.ParensContext { return x.cmds().toTyped("parens node: nil childe") } if let x = self as? ShellParser.ArgsContext { return (x.WORD()?.getText()).orFailure("args node: nil first word").map(ShellString.text) .combine { x.arg().mapAllOrFailures { $0.toTyped() }.mapError { $0.joinErrors() } } .map { [$0.0] + $0.1 } .map(Shell.args) } return .failure("Unknown node type: \(self)") } } extension ShellParser.ArgContext { func toTyped() -> Result, String> { if let x = self as? ShellParser.WordContext { return .success(.text(x.getText())) } if let x = self as? ShellParser.DQuotedStringContext { return x.dStringFragment().mapAllOrFailures { $0.toTyped() } .mapError { $0.joinErrors() }.map(ShellString.concatOptimized) } if let x = self as? ShellParser.SQuotedStringContext { return .success(.text(String(x.getText().dropFirst(1).dropLast(1)))) } if let x = self as? ShellParser.SubstitutionContext { return x.cmds().toTyped("substitution node: nil child").map(ShellString.interpolation) } return .failure("Unknown node type: \(self)") } } extension ShellParser.DStringFragmentContext { func toTyped() -> Result, String> { if let x = self as? ShellParser.EscapeSequenceContext { return switch x.getText() { case "\\n": .success(.text("\n")) case "\\t": .success(.text("\t")) case "\\$": .success(.text("$")) case "\\\"": .success(.text("\"")) case "\\\\": .success(.text("\\")) default: .failure("Unknown ESCAPE_SEQUENCE '\(x.getText())'") } } if let x = self as? ShellParser.TextContext { return .success(.text(x.getText())) } if let x = self as? ShellParser.InterpolationContext { return x.cmds().toTyped("interpolation node: nil child").map(ShellString.interpolation) } return .failure("Unknown node type: \(self)") } } private func binaryNode( _ op: (RawShell, RawShell) -> RawShell, _ a: ShellParser.CmdContext?, _ b: ShellParser.CmdContext?, ) -> Result { a.toTyped("binary node: nil child 0").combine { b.toTyped("binary node: nil child 1") }.map(op) } extension Result { func combine(_ other: () -> Result) -> Result<(Success, T), Failure> { flatMap { a in other().flatMap { b in .success((a, b)) } } } } extension Result where Success == ShellParser.CmdContext, Failure == String { func toTyped() -> Result { flatMap { $0.toTyped() } } } extension Result where Success == ShellParser.CmdsContext, Failure == String { func toTyped() -> Result { flatMap { $0.toTyped() } } } extension ShellParser.CmdContext? { fileprivate func toTyped(_ msg: String) -> Result { orFailure(msg).toTyped() } } extension ShellParser.CmdsContext? { fileprivate func toTyped(_ msg: String) -> Result { orFailure(msg).toTyped() } } // protocol AeroShell { // func run(_ state: CmdMutableState) -> CmdOut // } // extension [String] : AeroShell { // func run(_ state: CmdMutableState) -> CmdOut { .succ(self) } // } extension Shell: Equatable where T: Equatable {} typealias AeroShell = Shell typealias RawShell = Shell indirect enum Shell { case args([ShellString]) case empty case ifElse(IfElse) // Listed in precedence order case pipe(Shell, Shell) case and(Shell, Shell) case or(Shell, Shell) case seq([Shell]) static func ifElse(_ branches: (Shell, Shell?)..., elseB: Shell?) -> Shell { .ifElse(IfElse(branches: branches.map { Branch(cond: $0.0, then: $0.1) }, elseBranch: elseB)) } } extension IfElse: Equatable where T: Equatable {} struct IfElse { let branches: [Branch] let elseBranch: Shell? } extension Branch: Equatable where T: Equatable {} struct Branch { let cond: Shell let then: Shell? } extension ShellString: Equatable where T: Equatable {} enum ShellString { case text(String) case interpolation(Shell) case concat([ShellString]) static func concatOptimized(_ fragments: [ShellString]) -> ShellString { var result: [ShellString] = [] var current: String = "" _concatOptimized(fragments, &result, ¤t) if !current.isEmpty { result.append(.text(current)) } return result.singleOrNil() ?? .concat(result) } private static func _concatOptimized( _ fragments: [ShellString], _ result: inout [ShellString], _ current: inout String, ) { for fragment in fragments { switch fragment { case .text(let text): current += text case .concat(let newFragments): _concatOptimized(newFragments, &result, ¤t) case .interpolation: if !current.isEmpty { result.append(.text(current)) current = "" } result.append(fragment) } } } } ================================================ FILE: Sources/AppBundle/subscriptions.swift ================================================ import Common import Foundation import Network private struct Subscriber { let connection: NWConnection let events: Set } @MainActor private var subscribers: [UniqueToken: Subscriber] = [:] @MainActor func handleSubscribeAndWaitTillError(_ connection: NWConnection, _ args: SubscribeCmdArgs) async { let id = UniqueToken() subscribers[id] = Subscriber(connection: connection, events: args.events) defer { subscribers.removeValue(forKey: id) } if args.sendInitial { let f = focus for eventType in args.events { let event: ServerEvent switch eventType { case .focusChanged: event = .focusChanged(windowId: f.windowOrNil?.windowId, workspace: f.workspace.name) case .workspaceChanged: event = .workspaceChanged(workspace: f.workspace.name, prevWorkspace: f.workspace.name) case .modeChanged: event = .modeChanged(mode: activeMode) case .focusedMonitorChanged: event = .focusedMonitorChanged( workspace: f.workspace.name, monitorId_oneBased: f.workspace.workspaceMonitor.monitorId_oneBased ?? 0, ) case .windowDetected, .bindingTriggered: continue } if await connection.writeAtomic(event, jsonEncoder).error != nil { return } } } // Keep connection alive - wait for client to disconnect await connection.readTillError() } private let jsonEncoder: JSONEncoder = { let e = JSONEncoder() e.outputFormatting = [.withoutEscapingSlashes, .sortedKeys] return e }() func broadcastEvent(_ event: ServerEvent) { Task { @MainActor in for (id, subscriber) in subscribers { guard subscriber.events.contains(event.eventType) else { continue } if await subscriber.connection.writeAtomic(event, jsonEncoder).error != nil { _ = subscribers.removeValue(forKey: id) } } } } ================================================ FILE: Sources/AppBundle/tree/AbstractApp.swift ================================================ import Common protocol AbstractApp: AnyObject, Hashable, AeroAny { var pid: Int32 { get } var rawAppBundleId: String? { get } @MainActor func getFocusedWindow() async throws -> Window? var name: String? { get } var execPath: String? { get } var bundlePath: String? { get } } extension AbstractApp { static func == (lhs: Self, rhs: Self) -> Bool { if lhs.pid == rhs.pid { check(lhs === rhs) return true } else { check(lhs !== rhs) return false } } func hash(into hasher: inout Hasher) { hasher.combine(pid) } } extension Window { var macAppUnsafe: MacApp { app as! MacApp } } ================================================ FILE: Sources/AppBundle/tree/MacApp.swift ================================================ import AppKit import Common // Potential alternative implementation // https://github.com/swiftlang/swift-evolution/blob/main/proposals/0392-custom-actor-executors.md // (only available since macOS 14) final class MacApp: AbstractApp { /*conforms*/ let pid: Int32 /*conforms*/ let rawAppBundleId: String? let appId: KnownBundleId? let nsApp: NSRunningApplication private let axApp: ThreadGuardedValue private let appAxSubscriptions: ThreadGuardedValue<[AxSubscription]> // keep subscriptions in memory private let windows: ThreadGuardedValue<[UInt32: AxWindow]> = .init([:]) private var windowsCount = 0 var lastNativeFocusedWindowId: UInt32? = nil private var thread: Thread? private var setFrameJobs: [UInt32: RunLoopJob] = [:] @MainActor private static var focusJob: RunLoopJob? = nil /*conforms*/ var name: String? { nsApp.localizedName } /*conforms*/ var execPath: String? { nsApp.executableURL?.path } /*conforms*/ var bundlePath: String? { nsApp.bundleURL?.path } // todo think if it's possible to integrate this global mutable state to https://github.com/nikitabobko/AeroSpace/issues/1215 // and make deinitialization automatic in deinit @MainActor static var allAppsMap: [pid_t: MacApp] = [:] @MainActor private static var wipPids: [pid_t: AwaitableOneTimeBroadcastLatch] = [:] private init(_ nsApp: NSRunningApplication, _ axApp: AXUIElement, _ axSubscriptions: [AxSubscription], _ thread: Thread) { self.nsApp = nsApp self.axApp = .init(axApp) self.pid = nsApp.processIdentifier self.rawAppBundleId = nsApp.bundleIdentifier self.appId = nsApp.bundleIdentifier.flatMap { KnownBundleId.init(rawValue: $0) } assert(!axSubscriptions.isEmpty) self.appAxSubscriptions = .init(axSubscriptions) self.thread = thread } @MainActor @discardableResult static func getOrRegister(_ nsApp: NSRunningApplication) async throws -> MacApp? { // Don't perceive any of the lock screen windows as real windows // Otherwise, false positive ax notifications might trigger that lead to gcWindows if nsApp.bundleIdentifier == lockScreenAppBundleId { return nil } let pid = nsApp.processIdentifier // AX requests crash if you send them to yourself if pid == myPid { return nil } while true { if let existing = allAppsMap[pid] { return existing } try checkCancellation() if let wip = wipPids[pid] { try await wip.await() continue } let wip = AwaitableOneTimeBroadcastLatch() wipPids[pid] = wip let thread = Thread { $axTaskLocalAppThreadToken.withValue(AxAppThreadToken(pid: pid, idForDebug: nsApp.idForDebug)) { let axApp = AXUIElementCreateApplication(nsApp.processIdentifier) let handlers: HandlerToNotifKeyMapping = [ (refreshObs, [kAXWindowCreatedNotification, kAXFocusedWindowChangedNotification]), ] let job = RunLoopJob() let subscriptions = (try? AxSubscription.bulkSubscribe(nsApp, axApp, job, handlers)) ?? [] let isGood = !subscriptions.isEmpty let app = isGood ? MacApp(nsApp, axApp, subscriptions, Thread.current) : nil Task { @MainActor in allAppsMap[pid] = app await wip.signalToAll() wipPids[pid] = nil } if isGood { CFRunLoopRun() } } } thread.name = "AxAppThread \(nsApp.idForDebug)" thread.start() } } func closeAndUnregisterAxWindow(_ windowId: UInt32) { if serverArgs.isReadOnly { return } setFrameJobs.removeValue(forKey: windowId)?.cancel() _ = withWindowAsync(windowId) { [windows] window, job in guard let closeButton = window.get(Ax.closeButtonAttr) else { return } if AXUIElementPerformAction(closeButton.cast, kAXPressAction as CFString) == .success { windows.threadGuarded.removeValue(forKey: windowId) } } } func getAxSize(_ windowId: UInt32) async throws -> CGSize? { try await withWindow(windowId) { window, job in window.get(Ax.sizeAttr) } } // todo merge together with detectNewWindows func getFocusedWindow() async throws -> Window? { let windowId = try await thread?.runInLoop { [nsApp, axApp, windows] job in try axApp.threadGuarded.get(Ax.focusedWindowAttr) .flatMap { try windows.threadGuarded.getOrRegisterAxWindow(windowId: $0.windowId, $0.ax.cast, nsApp, job) }? .windowId } guard let windowId else { return nil } return try await MacWindow.getOrRegister(windowId: windowId, macApp: self) } @MainActor func nativeFocus(_ windowId: UInt32) { if serverArgs.isReadOnly { return } MacApp.focusJob?.cancel() // Performance optimization. If possible avoid doing AX requests // (important for apps which are slow at responding even such basic AX requests. E.g. Godot) // Beware of the macOS bug: https://github.com/nikitabobko/AeroSpace/issues/101 if (!NSScreen.screensHaveSeparateSpaces || monitors.count == 1) && (lastNativeFocusedWindowId == windowId || windowsCount == 1) { nsApp.activate(options: .activateIgnoringOtherApps) } else { MacApp.focusJob = withWindowAsync(windowId) { [nsApp] window, job in // Raise firstly to make sure that by the time we activate the app, the window would be already on top window.set(Ax.isMainAttr, true) AXUIElementPerformAction(window, kAXRaiseAction as CFString) nsApp.activate(options: .activateIgnoringOtherApps) } } } func setAxFrame(_ windowId: UInt32, _ topLeft: CGPoint?, _ size: CGSize?) { setFrameJobs.removeValue(forKey: windowId)?.cancel() setFrameJobs[windowId] = withWindowAsync(windowId) { [axApp] window, job in try disableAnimations(app: axApp.threadGuarded, job) { try setFrame(window, topLeft, size, job) } } } func setAxFrameBlocking(_ windowId: UInt32, _ topLeft: CGPoint?, _ size: CGSize?) async throws { setFrameJobs.removeValue(forKey: windowId)?.cancel() try await withWindow(windowId) { [axApp] window, job in try disableAnimations(app: axApp.threadGuarded, job) { try setFrame(window, topLeft, size, job) } } } func getAxWindowsCount() async throws -> Int? { try await thread?.runInLoop { [axApp] job in axApp.threadGuarded.get(Ax.windowsAttr)?.count } } func getAxRect(_ windowId: UInt32) async throws -> Rect? { try await withWindow(windowId) { window, job in guard let topLeftCorner = window.get(Ax.topLeftCornerAttr) else { return nil } guard let size = window.get(Ax.sizeAttr) else { return nil } return Rect(topLeftX: topLeftCorner.x, topLeftY: topLeftCorner.y, width: size.width, height: size.height) } } func isWindowHeuristic(_ windowId: UInt32, _ windowLevel: MacOsWindowLevel?) async throws -> Bool { return try await withWindow(windowId) { [nsApp, axApp, appId] window, job in window.isWindowHeuristic(axApp: axApp.threadGuarded, appId, nsApp.activationPolicy, windowLevel) } == true } func getAxUiElementWindowType(_ windowId: UInt32, _ windowLevel: MacOsWindowLevel?) async throws -> AxUiElementWindowType { return try await withWindow(windowId) { [nsApp, axApp, appId] window, job in window.getWindowType(axApp: axApp.threadGuarded, appId, nsApp.activationPolicy, windowLevel) } ?? .window } func isDialogHeuristic(_ windowId: UInt32, _ windowLevel: MacOsWindowLevel?) async throws -> Bool { try await withWindow(windowId) { [appId] window, job in window.isDialogHeuristic(appId, windowLevel) } == true } func setNativeFullscreen(_ windowId: UInt32, _ value: Bool) { setFrameJobs.removeValue(forKey: windowId)?.cancel() setFrameJobs[windowId] = withWindowAsync(windowId) { window, job in window.set(Ax.isFullscreenAttr, value) } } func setNativeMinimized(_ windowId: UInt32, _ value: Bool) { setFrameJobs.removeValue(forKey: windowId)?.cancel() setFrameJobs[windowId] = withWindowAsync(windowId) { window, job in window.set(Ax.minimizedAttr, value) } } func dumpWindowAxInfo(windowId: UInt32) async throws -> [String: Json] { try await withWindow(windowId) { window, job in dumpAxRecursive(window, .window) } ?? [:] } func dumpAppAxInfo() async throws -> [String: Json] { try await thread?.runInLoop { [axApp] job in dumpAxRecursive(axApp.threadGuarded, .app) } ?? [:] } func getAxTitle(_ windowId: UInt32) async throws -> String? { try await withWindow(windowId) { window, job in window.get(Ax.titleAttr) } } func isMacosNativeFullscreen(_ windowId: UInt32) async throws -> Bool? { try await withWindow(windowId) { window, job in window.get(Ax.isFullscreenAttr) } } func isMacosNativeMinimized(_ windowId: UInt32) async throws -> Bool? { try await withWindow(windowId) { window, job in window.get(Ax.minimizedAttr) } } @MainActor static func refreshAllAndGetAliveWindowIds(frontmostAppBundleId: String?) async throws -> [MacApp: [UInt32]] { for (_, app) in MacApp.allAppsMap { // gc dead apps try checkCancellation() if app.nsApp.isTerminated { await app.destroy() } } return try await withThrowingTaskGroup(of: (pid_t, [UInt32]).self, returning: [MacApp: [UInt32]].self) { group in func refreshTheApp(_ nsApp: NSRunningApplication) { group.addTask { @Sendable @MainActor in guard let app = try await MacApp.getOrRegister(nsApp) else { return (nsApp.processIdentifier, []) } return (nsApp.processIdentifier, try await app.refreshAndGetAliveWindowIds(frontmostAppBundleId: frontmostAppBundleId)) } } // Register new apps for nsApp in NSWorkspace.shared.runningApplications { try checkCancellation() if nsApp.activationPolicy == .regular { refreshTheApp(nsApp) } } for (_, app) in MacApp.allAppsMap { try checkCancellation() // "About this Mac" window, TouchID, and a lot of other utility windows // We don't monitor them actively as we do for regular apps, but if a window of one of those utility // apps got focused it will end up in allAppsMap if app.nsApp.activationPolicy != .regular { refreshTheApp(app.nsApp) } } var result: [MacApp: [UInt32]] = [:] for try await (pid, windowIds) in group { if let app = MacApp.allAppsMap[pid] { result[app] = windowIds } } return result } } private func refreshAndGetAliveWindowIds(frontmostAppBundleId: String?) async throws -> [UInt32] { if nsApp.isTerminated { await destroy() return [] } guard let thread else { return [] } let (alive, dead) = try await thread.runInLoop { [nsApp, windows, axApp] (job) -> ([UInt32], [UInt32]) in var alive: [UInt32: AxWindow] = windows.threadGuarded var dead = [UInt32: AxWindow]() // Second line of defence against lock screen. See the first line of defence: closedWindowsCache // Second and third lines of defence are technically needed only to avoid potential flickering if frontmostAppBundleId != lockScreenAppBundleId { (alive, dead) = try alive.partition { try job.checkCancellation() return $0.value.ax.containingWindowId() != nil } } for (id, window) in axApp.threadGuarded.get(Ax.windowsAttr) ?? [] { try job.checkCancellation() try alive.getOrRegisterAxWindow(windowId: id, window, nsApp, job) } windows.threadGuarded = alive return (Array(alive.keys), Array(dead.keys)) } windowsCount = alive.count for windowId in dead { setFrameJobs.removeValue(forKey: windowId)?.cancel() } return alive } private func destroy() async { _ = await Task { @MainActor [pid] in _ = MacApp.allAppsMap.removeValue(forKey: pid) }.result for (_, job) in setFrameJobs { job.cancel() } setFrameJobs = [:] thread?.runInLoopAsync { [windows, appAxSubscriptions, axApp] job in appAxSubscriptions.destroy() // Destroy AX objects in reverse order of their creation windows.destroy() axApp.destroy() CFRunLoopStop(CFRunLoopGetCurrent()) } thread = nil // Disallow all future job submissions } private func withWindow(_ windowId: UInt32, _ body: @Sendable @escaping (AXUIElement, RunLoopJob) throws -> T?) async throws -> T? { try await thread?.runInLoop { [windows] job in guard let window = windows.threadGuarded[windowId] else { return nil } return try body(window.ax, job) } } private func withWindowAsync(_ windowId: UInt32, _ body: @Sendable @escaping (AXUIElement, RunLoopJob) throws -> ()) -> RunLoopJob { thread?.runInLoopAsync { [windows] job in guard let window = windows.threadGuarded[windowId] else { return } try? body(window.ax, job) } ?? .cancelled } } private final class AxWindow { let windowId: UInt32 let ax: AXUIElement // periphery:ignore private let axSubscriptions: [AxSubscription] // keep subscriptions in memory private init(windowId: UInt32, _ ax: AXUIElement, _ axSubscriptions: [AxSubscription]) { self.windowId = windowId self.ax = ax assert(!axSubscriptions.isEmpty) self.axSubscriptions = axSubscriptions } static func new(windowId: UInt32, _ ax: AXUIElement, _ nsApp: NSRunningApplication, _ job: RunLoopJob) throws -> AxWindow? { let handlers: HandlerToNotifKeyMapping = [ (refreshObs, [kAXUIElementDestroyedNotification, kAXWindowDeminiaturizedNotification, kAXWindowMiniaturizedNotification]), (movedObs, [kAXMovedNotification]), (resizedObs, [kAXResizedNotification]), ] let subscriptions = try AxSubscription.bulkSubscribe(nsApp, ax, job, handlers) return !subscriptions.isEmpty ? AxWindow(windowId: windowId, ax, subscriptions) : nil } } extension [UInt32: AxWindow] { @discardableResult fileprivate mutating func getOrRegisterAxWindow(windowId id: UInt32, _ axWindow: AXUIElement, _ nsApp: NSRunningApplication, _ job: RunLoopJob) throws -> AxWindow? { if let existing = self[id] { return existing } // Delay new window detection if mouse is down // It helps with apps that allow dragging their tabs out to create new windows // https://github.com/nikitabobko/AeroSpace/issues/1001 if isLeftMouseButtonDown { return nil } if let window = try AxWindow.new(windowId: id, axWindow, nsApp, job) { self[id] = window return window } else { return nil } } } private func setFrame(_ window: AXUIElement, _ topLeft: CGPoint?, _ size: CGSize?, _ job: RunLoopJob) throws { // Set size and then the position. The order is important https://github.com/nikitabobko/AeroSpace/issues/143 // https://github.com/nikitabobko/AeroSpace/issues/335 if let size { window.set(Ax.sizeAttr, size) } try job.checkCancellation() if let topLeft { window.set(Ax.topLeftCornerAttr, topLeft) } else { return } try job.checkCancellation() if let size { window.set(Ax.sizeAttr, size) } } // Some undocumented magic // References: https://github.com/koekeishiya/yabai/commit/3fe4c77b001e1a4f613c26f01ea68c0f09327f3a // https://github.com/rxhanson/Rectangle/pull/285 private func disableAnimations(app: AXUIElement, _ job: RunLoopJob, _ body: () throws -> T) throws -> T { let wasEnabled = app.get(Ax.enhancedUserInterfaceAttr) == true if wasEnabled { app.set(Ax.enhancedUserInterfaceAttr, false) } defer { if wasEnabled { app.set(Ax.enhancedUserInterfaceAttr, true) } } try job.checkCancellation() return try body() } ================================================ FILE: Sources/AppBundle/tree/MacWindow.swift ================================================ import AppKit import Common final class MacWindow: Window { let macApp: MacApp private var prevUnhiddenProportionalPositionInsideWorkspaceRect: CGPoint? @MainActor private init(_ id: UInt32, _ actor: MacApp, lastFloatingSize: CGSize?, parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) { self.macApp = actor super.init(id: id, actor, lastFloatingSize: lastFloatingSize, parent: parent, adaptiveWeight: adaptiveWeight, index: index) } @MainActor static var allWindowsMap: [UInt32: MacWindow] = [:] @MainActor static var allWindows: [MacWindow] { Array(allWindowsMap.values) } @MainActor @discardableResult static func getOrRegister(windowId: UInt32, macApp: MacApp) async throws -> MacWindow { if let existing = allWindowsMap[windowId] { return existing } let rect = try await macApp.getAxRect(windowId) let data = try await unbindAndGetBindingDataForNewWindow( windowId, macApp, isStartup ? (rect?.center.monitorApproximation ?? mainMonitor).activeWorkspace : focus.workspace, window: nil, ) // atomic synchronous section if let existing = allWindowsMap[windowId] { return existing } let window = MacWindow(windowId, macApp, lastFloatingSize: rect?.size, parent: data.parent, adaptiveWeight: data.adaptiveWeight, index: data.index) allWindowsMap[windowId] = window try await debugWindowsIfRecording(window) if try await !restoreClosedWindowsCacheIfNeeded(newlyDetectedWindow: window) { try await tryOnWindowDetected(window) } return window } // var description: String { // let description = [ // ("title", title), // ("role", axWindow.get(Ax.roleAttr)), // ("subrole", axWindow.get(Ax.subroleAttr)), // ("identifier", axWindow.get(Ax.identifierAttr)), // ("modal", axWindow.get(Ax.modalAttr).map { String($0) } ?? ""), // ("windowId", String(windowId)), // ].map { "\($0.0): '\(String(describing: $0.1))'" }.joined(separator: ", ") // return "Window(\(description))" // } func isWindowHeuristic(_ windowLevel: MacOsWindowLevel?) async throws -> Bool { // todo cache try await macApp.isWindowHeuristic(windowId, windowLevel) } func isDialogHeuristic(_ windowLevel: MacOsWindowLevel?) async throws -> Bool { // todo cache try await macApp.isDialogHeuristic(windowId, windowLevel) } func dumpAxInfo() async throws -> [String: Json] { try await macApp.dumpWindowAxInfo(windowId: windowId) } func setNativeFullscreen(_ value: Bool) { macApp.setNativeFullscreen(windowId, value) } func setNativeMinimized(_ value: Bool) { macApp.setNativeMinimized(windowId, value) } // skipClosedWindowsCache is an optimization when it's definitely not necessary to cache closed window. // If you are unsure, it's better to pass `false` @MainActor func garbageCollect(skipClosedWindowsCache: Bool) { if MacWindow.allWindowsMap.removeValue(forKey: windowId) == nil { return } if !skipClosedWindowsCache { cacheClosedWindowIfNeeded() } let parent = unbindFromParent().parent let deadWindowWorkspace = parent.nodeWorkspace let focus = focus if let deadWindowWorkspace, deadWindowWorkspace == focus.workspace || deadWindowWorkspace == prevFocusedWorkspace && prevFocusedWorkspaceDate.distance(to: .now) < 1 { switch parent.cases { case .tilingContainer, .workspace, .macosHiddenAppsWindowsContainer, .macosFullscreenWindowsContainer: let deadWindowFocus = deadWindowWorkspace.toLiveFocus() _ = setFocus(to: deadWindowFocus) // Guard against "Apple Reminders popup" bug: https://github.com/nikitabobko/AeroSpace/issues/201 if focus.windowOrNil?.app.pid != app.pid { // Force focus to fix macOS annoyance with focused apps without windows. // https://github.com/nikitabobko/AeroSpace/issues/65 deadWindowFocus.windowOrNil?.nativeFocus() } case .macosPopupWindowsContainer, .macosMinimizedWindowsContainer: break // Don't switch back on popup destruction } } } @MainActor override var title: String { get async throws { try await macApp.getAxTitle(windowId) ?? "" } } @MainActor override var isMacosFullscreen: Bool { get async throws { try await macApp.isMacosNativeFullscreen(windowId) == true } } @MainActor override var isMacosMinimized: Bool { get async throws { try await macApp.isMacosNativeMinimized(windowId) == true } } @MainActor override func nativeFocus() { macApp.nativeFocus(windowId) } override func closeAxWindow() { garbageCollect(skipClosedWindowsCache: true) macApp.closeAndUnregisterAxWindow(windowId) } // todo it's part of the window layout and should be moved to layoutRecursive.swift @MainActor func hideInCorner(_ corner: OptimalHideCorner) async throws { guard let nodeMonitor else { return } // Don't accidentally override prevUnhiddenEmulationPosition in case of subsequent `hideInCorner` calls if !isHiddenInCorner { guard let windowRect = try await getAxRect() else { return } // Check for isHiddenInCorner for the second time because of the suspension point above if !isHiddenInCorner { let topLeftCorner = windowRect.topLeftCorner let monitorRect = windowRect.center.monitorApproximation.rect // Similar to layoutFloatingWindow. Non idempotent let absolutePoint = topLeftCorner - monitorRect.topLeftCorner prevUnhiddenProportionalPositionInsideWorkspaceRect = CGPoint(x: absolutePoint.x / monitorRect.width, y: absolutePoint.y / monitorRect.height) } } let p: CGPoint switch corner { case .bottomLeftCorner: guard let s = try await getAxSize() else { fallthrough } // Zoom will jump off if you do one pixel offset https://github.com/nikitabobko/AeroSpace/issues/527 // todo this ad hoc won't be necessary once I implement optimization suggested by Zalim let onePixelOffset = macApp.appId == .zoom ? .zero : CGPoint(x: 1, y: -1) p = nodeMonitor.visibleRect.bottomLeftCorner + onePixelOffset + CGPoint(x: -s.width, y: 0) case .bottomRightCorner: // Zoom will jump off if you do one pixel offset https://github.com/nikitabobko/AeroSpace/issues/527 // todo this ad hoc won't be necessary once I implement optimization suggested by Zalim let onePixelOffset = macApp.appId == .zoom ? .zero : CGPoint(x: 1, y: 1) p = nodeMonitor.visibleRect.bottomRightCorner - onePixelOffset } setAxFrame(p, nil) } @MainActor func unhideFromCorner() { guard let prevUnhiddenProportionalPositionInsideWorkspaceRect else { return } guard let nodeWorkspace else { return } // hiding only makes sense for workspace windows guard let parent else { return } switch getChildParentRelation(child: self, parent: parent) { // Just a small optimization to avoid unnecessary AX calls for non floating windows // Tiling windows should be unhidden with layoutRecursive anyway case .floatingWindow: let workspaceRect = nodeWorkspace.workspaceMonitor.rect var newX = workspaceRect.topLeftX + workspaceRect.width * prevUnhiddenProportionalPositionInsideWorkspaceRect.x var newY = workspaceRect.topLeftY + workspaceRect.height * prevUnhiddenProportionalPositionInsideWorkspaceRect.y // todo we probably should replace lastFloatingSize with proper floating window sizing // https://github.com/nikitabobko/AeroSpace/issues/1519 let windowWidth = lastFloatingSize?.width ?? 0 let windowHeight = lastFloatingSize?.height ?? 0 newX = newX.coerce(in: workspaceRect.minX ... max(workspaceRect.minX, workspaceRect.maxX - windowWidth)) newY = newY.coerce(in: workspaceRect.minY ... max(workspaceRect.minY, workspaceRect.maxY - windowHeight)) setAxFrame(CGPoint(x: newX, y: newY), nil) case .macosNativeFullscreenWindow, .macosNativeHiddenAppWindow, .macosNativeMinimizedWindow, .macosPopupWindow, .tiling, .rootTilingContainer, .shimContainerRelation: break } self.prevUnhiddenProportionalPositionInsideWorkspaceRect = nil } override var isHiddenInCorner: Bool { prevUnhiddenProportionalPositionInsideWorkspaceRect != nil } override func getAxSize() async throws -> CGSize? { try await macApp.getAxSize(windowId) } override func setAxFrame(_ topLeft: CGPoint?, _ size: CGSize?) { macApp.setAxFrame(windowId, topLeft, size) } func setAxFrameBlocking(_ topLeft: CGPoint?, _ size: CGSize?) async throws { try await macApp.setAxFrameBlocking(windowId, topLeft, size) } override func getAxRect() async throws -> Rect? { try await macApp.getAxRect(windowId) } } extension Window { @MainActor func relayoutWindow(on workspace: Workspace, forceTile: Bool = false) async throws { let data = forceTile ? unbindAndGetBindingDataForNewTilingWindow(workspace, window: self) : try await unbindAndGetBindingDataForNewWindow(self.asMacWindow().windowId, self.asMacWindow().macApp, workspace, window: self) bind(to: data.parent, adaptiveWeight: data.adaptiveWeight, index: data.index) } } // The function is private because it's unsafe. It leaves the window in unbound state @MainActor private func unbindAndGetBindingDataForNewWindow(_ windowId: UInt32, _ macApp: MacApp, _ workspace: Workspace, window: Window?) async throws -> BindingData { let windowLevel = getWindowLevel(for: windowId) return switch try await macApp.getAxUiElementWindowType(windowId, windowLevel) { case .popup: BindingData(parent: macosPopupWindowsContainer, adaptiveWeight: WEIGHT_AUTO, index: INDEX_BIND_LAST) case .dialog: BindingData(parent: workspace, adaptiveWeight: WEIGHT_AUTO, index: INDEX_BIND_LAST) case .window: unbindAndGetBindingDataForNewTilingWindow(workspace, window: window) } } // The function is private because it's unsafe. It leaves the window in unbound state @MainActor private func unbindAndGetBindingDataForNewTilingWindow(_ workspace: Workspace, window: Window?) -> BindingData { window?.unbindFromParent() // It's important to unbind to get correct data from below let mruWindow = workspace.mostRecentWindowRecursive if let mruWindow, let tilingParent = mruWindow.parent as? TilingContainer { return BindingData( parent: tilingParent, adaptiveWeight: WEIGHT_AUTO, index: mruWindow.ownIndex.orDie() + 1, ) } else { return BindingData( parent: workspace.rootTilingContainer, adaptiveWeight: WEIGHT_AUTO, index: INDEX_BIND_LAST, ) } } @MainActor func tryOnWindowDetected(_ window: Window) async throws { guard let parent = window.parent else { return } switch parent.cases { case .tilingContainer, .workspace, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer: try await onWindowDetected(window) case .macosPopupWindowsContainer: break } } @MainActor private func onWindowDetected(_ window: Window) async throws { broadcastEvent(.windowDetected( windowId: window.windowId, workspace: window.nodeWorkspace?.name, appBundleId: window.app.rawAppBundleId, appName: window.app.name, )) for callback in config.onWindowDetected where try await callback.matches(window) { _ = try await callback.run.runCmdSeq(.defaultEnv.copy(\.windowId, window.windowId), .emptyStdin) if !callback.checkFurtherCallbacks { return } } } extension WindowDetectedCallback { @MainActor func matches(_ window: Window) async throws -> Bool { if let startupMatcher = matcher.duringAeroSpaceStartup, startupMatcher != isStartup { return false } if let regex = matcher.windowTitleRegexSubstring, !(try await window.title).contains(regex) { return false } if let appId = matcher.appId, appId != window.app.rawAppBundleId { return false } if let regex = matcher.appNameRegexSubstring, !(window.app.name ?? "").contains(regex) { return false } if let workspace = matcher.workspace, workspace != window.nodeWorkspace?.name { return false } return true } } ================================================ FILE: Sources/AppBundle/tree/MacosUnconventionalWindowsContainer.swift ================================================ import Common final class MacosFullscreenWindowsContainer: TreeNode, NonLeafTreeNodeObject { @MainActor init(parent: Workspace) { super.init(parent: parent, adaptiveWeight: 1, index: INDEX_BIND_LAST) } } /// The container for macOS windows of hidden apps final class MacosHiddenAppsWindowsContainer: TreeNode, NonLeafTreeNodeObject { @MainActor init(parent: Workspace) { super.init(parent: parent, adaptiveWeight: 1, index: INDEX_BIND_LAST) } } @MainActor let macosMinimizedWindowsContainer = MacosMinimizedWindowsContainer() final class MacosMinimizedWindowsContainer: TreeNode, NonLeafTreeNodeObject { @MainActor fileprivate init() { super.init(parent: NilTreeNode.instance, adaptiveWeight: 1, index: INDEX_BIND_LAST) } } @MainActor let macosPopupWindowsContainer = MacosPopupWindowsContainer() /// The container for macOS objects that are windows from AX perspective but from human perspective they are not even /// dialogs. E.g. Sonoma (macOS 14) keyboard layout switch final class MacosPopupWindowsContainer: TreeNode, NonLeafTreeNodeObject { @MainActor fileprivate init() { super.init(parent: NilTreeNode.instance, adaptiveWeight: 1, index: INDEX_BIND_LAST) } } ================================================ FILE: Sources/AppBundle/tree/TilingContainer.swift ================================================ import AppKit import Common final class TilingContainer: TreeNode, NonLeafTreeNodeObject { // todo consider renaming to GenericContainer fileprivate var _orientation: Orientation var orientation: Orientation { _orientation } var layout: Layout @MainActor init(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, _ orientation: Orientation, _ layout: Layout, index: Int) { self._orientation = orientation self.layout = layout super.init(parent: parent, adaptiveWeight: adaptiveWeight, index: index) } @MainActor static func newHTiles(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) -> TilingContainer { TilingContainer(parent: parent, adaptiveWeight: adaptiveWeight, .h, .tiles, index: index) } @MainActor static func newVTiles(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) -> TilingContainer { TilingContainer(parent: parent, adaptiveWeight: adaptiveWeight, .v, .tiles, index: index) } } extension TilingContainer { var isRootContainer: Bool { parent is Workspace } @MainActor func changeOrientation(_ targetOrientation: Orientation) { if orientation == targetOrientation { return } if config.enableNormalizationOppositeOrientationForNestedContainers { var orientation = targetOrientation parentsWithSelf .filterIsInstance(of: TilingContainer.self) .forEach { $0._orientation = orientation orientation = orientation.opposite } } else { _orientation = targetOrientation } } func normalizeOppositeOrientationForNestedContainers() { if orientation == (parent as? TilingContainer)?.orientation { _orientation = orientation.opposite } for child in children { (child as? TilingContainer)?.normalizeOppositeOrientationForNestedContainers() } } } enum Layout: String { case tiles case accordion } extension String { func parseLayout() -> Layout? { if let parsed = Layout(rawValue: self) { return parsed } else if self == "list" { return .tiles } else { return nil } } } ================================================ FILE: Sources/AppBundle/tree/TreeNode.swift ================================================ import AppKit import Common open class TreeNode: Equatable, AeroAny { private var _children: [TreeNode] = [] var children: [TreeNode] { _children } fileprivate final weak var _parent: NonLeafTreeNodeObject? = nil final var parent: NonLeafTreeNodeObject? { _parent } private var adaptiveWeight: CGFloat private let _mruChildren: MruStack = MruStack() // Usages: // - resize with mouse // - makeFloatingWindowsSeenAsTiling in focus command var lastAppliedLayoutVirtualRect: Rect? = nil // as if inner gaps were always zero // Usages: // - resize with mouse // - drag window with mouse // - move-mouse command var lastAppliedLayoutPhysicalRect: Rect? = nil // with real inner gaps final var unboundStacktrace: String? = nil var isBound: Bool { parent != nil } // todo drop, once https://github.com/nikitabobko/AeroSpace/issues/1215 is fixed @MainActor init(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) { self.adaptiveWeight = adaptiveWeight bind(to: parent, adaptiveWeight: adaptiveWeight, index: index) } fileprivate init() { adaptiveWeight = 0 } /// See: ``getWeight(_:)`` func setWeight(_ targetOrientation: Orientation, _ newValue: CGFloat) { guard let parent else { die("Can't change weight if TreeNode doesn't have parent") } switch getChildParentRelation(child: self, parent: parent) { case .tiling(let parent): if parent.orientation != targetOrientation { die("You can't change \(targetOrientation) weight of nodes located in \(parent.orientation) container") } if parent.layout != .tiles { die("Weight can be changed only for nodes whose parent has 'tiles' layout") } adaptiveWeight = newValue default: die("Can't change weight") } } /// Weight itself doesn't make sense. The parent container controls semantics of weight @MainActor func getWeight(_ targetOrientation: Orientation) -> CGFloat { guard let parent else { die("Weight doesn't make sense for containers without parent") } return switch getChildParentRelation(child: self, parent: parent) { case .tiling(let parent): parent.orientation == targetOrientation ? adaptiveWeight : parent.getWeight(targetOrientation) case .rootTilingContainer: parent.getWeight(targetOrientation) case .floatingWindow, .macosNativeFullscreenWindow: dieT("Weight doesn't make sense for floating windows") case .macosNativeMinimizedWindow: dieT("Weight doesn't make sense for minimized windows") case .macosPopupWindow: dieT("Weight doesn't make sense for popup windows") case .macosNativeHiddenAppWindow: dieT("Weight doesn't make sense for windows of hidden apps") case .shimContainerRelation: dieT("Weight doesn't make sense for stub containers") } } @MainActor @discardableResult func bind(to newParent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) -> BindingData? { let result = unbindIfBound() if newParent === NilTreeNode.instance { return result } let relation = getChildParentRelation(child: self, parent: newParent) // Side effect: verify relation if adaptiveWeight == WEIGHT_AUTO { self.adaptiveWeight = switch relation { case .tiling(let newParent): CGFloat(newParent.children.sumOfDouble { $0.getWeight(newParent.orientation) }).div(newParent.children.count) ?? 1 case .floatingWindow, .macosNativeFullscreenWindow, .rootTilingContainer, .macosNativeMinimizedWindow, .shimContainerRelation, .macosPopupWindow, .macosNativeHiddenAppWindow: WEIGHT_DOESNT_MATTER } } else { self.adaptiveWeight = adaptiveWeight } newParent._children.insert(self, at: index != INDEX_BIND_LAST ? index : newParent._children.count) _parent = newParent unboundStacktrace = nil // todo consider disabling automatic mru propogation // 1. "floating windows" in FocusCommand break the MRU because of that :( // 2. Misbehaved apps that abuse real window as popups https://github.com/nikitabobko/AeroSpace/issues/106 (the // last appeared window, is not necessarily the one that has the focus) markAsMostRecentChild() return result } private func unbindIfBound() -> BindingData? { guard let _parent else { return nil } let index = _parent._children.remove(element: self) ?? dieT("Can't find child in its parent") check(_parent._mruChildren.remove(self)) self._parent = nil unboundStacktrace = getStringStacktrace() return BindingData(parent: _parent, adaptiveWeight: adaptiveWeight, index: index) } func markAsMostRecentChild() { guard let _parent else { return } _parent._mruChildren.pushOrRaise(self) _parent.markAsMostRecentChild() } var mostRecentChild: TreeNode? { _mruChildren.mostRecent ?? children.last } @discardableResult func unbindFromParent() -> BindingData { unbindIfBound() ?? dieT("\(self) is already unbound. The stacktrace where it was unbound:\n\(unboundStacktrace ?? "nil")") } nonisolated public static func == (lhs: TreeNode, rhs: TreeNode) -> Bool { lhs === rhs } private var userData: [String: Any] = [:] func getUserData(key: TreeNodeUserDataKey) -> T? { userData[key.key] as! T? } func putUserData(key: TreeNodeUserDataKey, data: T) { userData[key.key] = data } @discardableResult func cleanUserData(key: TreeNodeUserDataKey) -> T? { userData.removeValue(forKey: key.key) as! T? } } // periphery:ignore - Generic T is used struct TreeNodeUserDataKey { let key: String } let WEIGHT_DOESNT_MATTER = CGFloat(-2) /// Splits containers evenly if tiling. /// /// Reset weight is bind to workspace (aka "floating windows") let WEIGHT_AUTO = CGFloat(-1) let INDEX_BIND_LAST = -1 struct BindingData { let parent: NonLeafTreeNodeObject let adaptiveWeight: CGFloat let index: Int } final class NilTreeNode: TreeNode, NonLeafTreeNodeObject { override private init() { super.init() } @MainActor static let instance = NilTreeNode() } ================================================ FILE: Sources/AppBundle/tree/TreeNodeCases.swift ================================================ import Common enum TreeNodeCases { case window(Window) case tilingContainer(TilingContainer) case workspace(Workspace) case macosMinimizedWindowsContainer(MacosMinimizedWindowsContainer) case macosHiddenAppsWindowsContainer(MacosHiddenAppsWindowsContainer) case macosFullscreenWindowsContainer(MacosFullscreenWindowsContainer) case macosPopupWindowsContainer(MacosPopupWindowsContainer) } enum NonLeafTreeNodeCases { case tilingContainer(TilingContainer) case workspace(Workspace) case macosMinimizedWindowsContainer(MacosMinimizedWindowsContainer) case macosHiddenAppsWindowsContainer(MacosHiddenAppsWindowsContainer) case macosFullscreenWindowsContainer(MacosFullscreenWindowsContainer) case macosPopupWindowsContainer(MacosPopupWindowsContainer) } enum TilingTreeNodeCases { case window(Window) case tilingContainer(TilingContainer) } enum NonLeafTreeNodeKind: Equatable { case tilingContainer case workspace case macosMinimizedWindowsContainer case macosHiddenAppsWindowsContainer case macosFullscreenWindowsContainer case macosPopupWindowsContainer } protocol NonLeafTreeNodeObject: TreeNode {} extension TreeNode { var nodeCases: TreeNodeCases { if let window = self as? Window { return .window(window) } else if let workspace = self as? Workspace { return .workspace(workspace) } else if let tilingContainer = self as? TilingContainer { return .tilingContainer(tilingContainer) } else if let container = self as? MacosHiddenAppsWindowsContainer { return .macosHiddenAppsWindowsContainer(container) } else if let container = self as? MacosMinimizedWindowsContainer { return .macosMinimizedWindowsContainer(container) } else if let container = self as? MacosFullscreenWindowsContainer { return .macosFullscreenWindowsContainer(container) } else if let container = self as? MacosPopupWindowsContainer { return .macosPopupWindowsContainer(container) } else { die("Unknown tree") } } func tilingTreeNodeCasesOrDie() -> TilingTreeNodeCases { if let window = self as? Window { return .window(window) } else if let tilingContainer = self as? TilingContainer { return .tilingContainer(tilingContainer) } else { illegalChildParentRelation(child: self, parent: parent) } } } extension NonLeafTreeNodeObject { var cases: NonLeafTreeNodeCases { if self is Window { die("Windows are leaf nodes. They can't have children") } else if let workspace = self as? Workspace { return .workspace(workspace) } else if let tilingContainer = self as? TilingContainer { return .tilingContainer(tilingContainer) } else if let container = self as? MacosMinimizedWindowsContainer { return .macosMinimizedWindowsContainer(container) } else if let container = self as? MacosHiddenAppsWindowsContainer { return .macosHiddenAppsWindowsContainer(container) } else if let container = self as? MacosFullscreenWindowsContainer { return .macosFullscreenWindowsContainer(container) } else if let container = self as? MacosPopupWindowsContainer { return .macosPopupWindowsContainer(container) } else { die("Unknown tree \(self)") } } var kind: NonLeafTreeNodeKind { return switch cases { case .tilingContainer: .tilingContainer case .workspace: .workspace case .macosMinimizedWindowsContainer: .macosMinimizedWindowsContainer case .macosFullscreenWindowsContainer: .macosFullscreenWindowsContainer case .macosHiddenAppsWindowsContainer: .macosHiddenAppsWindowsContainer case .macosPopupWindowsContainer: .macosPopupWindowsContainer } } } enum ChildParentRelation: Equatable { case floatingWindow case macosNativeFullscreenWindow case macosNativeHiddenAppWindow case macosNativeMinimizedWindow case macosPopupWindow case tiling(parent: TilingContainer) // todo consider splitting it on 'tiles' and 'accordion' case rootTilingContainer case shimContainerRelation } func getChildParentRelation(child: TreeNode, parent: NonLeafTreeNodeObject) -> ChildParentRelation { if let relation = getChildParentRelationOrNil(child: child, parent: parent) { return relation } illegalChildParentRelation(child: child, parent: parent) } func illegalChildParentRelation(child: TreeNode, parent: NonLeafTreeNodeObject?) -> Never { die("Illegal child-parent relation. Child: \(child), Parent: \((parent ?? child.parent).prettyDescription)") } func getChildParentRelationOrNil(child: TreeNode, parent: NonLeafTreeNodeObject) -> ChildParentRelation? { return switch (child.nodeCases, parent.cases) { case (.workspace, _): nil case (.window, .workspace): .floatingWindow case (.window, .macosPopupWindowsContainer): .macosPopupWindow case (_, .macosPopupWindowsContainer): nil case (.macosPopupWindowsContainer, _): nil case (.window, .macosMinimizedWindowsContainer): .macosNativeMinimizedWindow case (_, .macosMinimizedWindowsContainer): nil case (.macosMinimizedWindowsContainer, _): nil case (.tilingContainer, .tilingContainer(let container)), (.window, .tilingContainer(let container)): .tiling(parent: container) case (.tilingContainer, .workspace): .rootTilingContainer case (.macosFullscreenWindowsContainer, .workspace): .shimContainerRelation case (.window, .macosFullscreenWindowsContainer): .macosNativeFullscreenWindow case (.macosFullscreenWindowsContainer, _): nil case (_, .macosFullscreenWindowsContainer): nil case (.macosHiddenAppsWindowsContainer, .workspace): .shimContainerRelation case (.window, .macosHiddenAppsWindowsContainer): .macosNativeHiddenAppWindow case (.macosHiddenAppsWindowsContainer, _): nil case (_, .macosHiddenAppsWindowsContainer): nil } } ================================================ FILE: Sources/AppBundle/tree/TreeNodeEx.swift ================================================ import AppKit import Common extension TreeNode { private func visit(node: TreeNode, result: inout [Window]) { if let node = node as? Window { result.append(node) } for child in node.children { visit(node: child, result: &result) } } var allLeafWindowsRecursive: [Window] { var result: [Window] = [] visit(node: self, result: &result) return result } var ownIndex: Int? { guard let parent else { return nil } return parent.children.firstIndex(of: self).orDie() } var parents: [NonLeafTreeNodeObject] { parent.flatMap { [$0] + $0.parents } ?? [] } var parentsWithSelf: [TreeNode] { parent.flatMap { [self] + $0.parentsWithSelf } ?? [self] } /// Also see visualWorkspace var nodeWorkspace: Workspace? { self as? Workspace ?? parent?.nodeWorkspace } /// Also see: workspace @MainActor var visualWorkspace: Workspace? { nodeWorkspace ?? nodeMonitor?.activeWorkspace } @MainActor var nodeMonitor: Monitor? { switch self.nodeCases { case .workspace(let ws): ws.workspaceMonitor case .window: parent?.nodeMonitor case .tilingContainer: parent?.nodeMonitor case .macosFullscreenWindowsContainer: parent?.nodeMonitor case .macosHiddenAppsWindowsContainer: parent?.nodeMonitor case .macosMinimizedWindowsContainer, .macosPopupWindowsContainer: nil } } var mostRecentWindowRecursive: Window? { self as? Window ?? mostRecentChild?.mostRecentWindowRecursive } var anyLeafWindowRecursive: Window? { if let window = self as? Window { return window } for child in children { if let window = child.anyLeafWindowRecursive { return window } } return nil } // Doesn't contain at least one window var isEffectivelyEmpty: Bool { anyLeafWindowRecursive == nil } @MainActor var hWeight: CGFloat { get { getWeight(.h) } set { setWeight(.h, newValue) } } @MainActor var vWeight: CGFloat { get { getWeight(.v) } set { setWeight(.v, newValue) } } /// Returns closest parent that has children in the specified direction relative to `self` func closestParent( hasChildrenInDirection direction: CardinalDirection, withLayout layout: Layout?, ) -> (parent: TilingContainer, ownIndex: Int)? { let innermostChild = parentsWithSelf.first(where: { (node: TreeNode) -> Bool in return switch node.parent?.cases { // stop searching. We didn't find it, or something went wrong case .workspace, nil, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer, .macosPopupWindowsContainer: true case .tilingContainer(let parent): (layout == nil || parent.layout == layout) && parent.orientation == direction.orientation && (node.ownIndex.map { parent.children.indices.contains($0 + direction.focusOffset) } ?? true) } }) guard let innermostChild else { return nil } switch innermostChild.parent?.cases { case .tilingContainer(let parent): check(parent.orientation == direction.orientation) return innermostChild.ownIndex.map { (parent, $0) } case .workspace, nil, .macosMinimizedWindowsContainer, .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer, .macosPopupWindowsContainer: return nil } } } ================================================ FILE: Sources/AppBundle/tree/Window.swift ================================================ import AppKit import Common open class Window: TreeNode, Hashable { let windowId: UInt32 let app: any AbstractApp var lastFloatingSize: CGSize? var isFullscreen: Bool = false var noOuterGapsInFullscreen: Bool = false var layoutReason: LayoutReason = .standard @MainActor init(id: UInt32, _ app: any AbstractApp, lastFloatingSize: CGSize?, parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat, index: Int) { self.windowId = id self.app = app self.lastFloatingSize = lastFloatingSize super.init(parent: parent, adaptiveWeight: adaptiveWeight, index: index) } @MainActor static func get(byId windowId: UInt32) -> Window? { // todo make non optional isUnitTest ? Workspace.all.flatMap { $0.allLeafWindowsRecursive }.first(where: { $0.windowId == windowId }) : MacWindow.allWindowsMap[windowId] } @MainActor func closeAxWindow() { die("Not implemented") } public func hash(into hasher: inout Hasher) { hasher.combine(windowId) } func getAxSize() async throws -> CGSize? { die("Not implemented") } var title: String { get async throws { die("Not implemented") } } var isMacosFullscreen: Bool { get async throws { false } } var isMacosMinimized: Bool { get async throws { false } } // todo replace with enum MacOsWindowNativeState { normal, fullscreen, invisible } var isHiddenInCorner: Bool { die("Not implemented") } @MainActor func nativeFocus() { die("Not implemented") } func getAxRect() async throws -> Rect? { die("Not implemented") } func getCenter() async throws -> CGPoint? { try await getAxRect()?.center } func setAxFrame(_ topLeft: CGPoint?, _ size: CGSize?) { die("Not implemented") } } enum LayoutReason: Equatable { case standard /// Reason for the cur temp layout is macOS native fullscreen, minimize, or hide case macos(prevParentKind: NonLeafTreeNodeKind) } extension Window { var isFloating: Bool { parent is Workspace } // todo drop. It will be a source of bugs when sticky is introduced @discardableResult @MainActor func bindAsFloatingWindow(to workspace: Workspace) -> BindingData? { bind(to: workspace, adaptiveWeight: WEIGHT_AUTO, index: INDEX_BIND_LAST) } func asMacWindow() -> MacWindow { self as! MacWindow } } ================================================ FILE: Sources/AppBundle/tree/Workspace.swift ================================================ import AppKit import Common @MainActor private var workspaceNameToWorkspace: [String: Workspace] = [:] @MainActor private var screenPointToPrevVisibleWorkspace: [CGPoint: String] = [:] @MainActor private var screenPointToVisibleWorkspace: [CGPoint: Workspace] = [:] @MainActor private var visibleWorkspaceToScreenPoint: [Workspace: CGPoint] = [:] // The returned workspace must be invisible and it must belong to the requested monitor @MainActor func getStubWorkspace(for monitor: Monitor) -> Workspace { getStubWorkspace(forPoint: monitor.rect.topLeftCorner) } @MainActor private func getStubWorkspace(forPoint point: CGPoint) -> Workspace { if let prev = screenPointToPrevVisibleWorkspace[point].map({ Workspace.get(byName: $0) }), !prev.isVisible && prev.workspaceMonitor.rect.topLeftCorner == point && prev.forceAssignedMonitor == nil { return prev } if let candidate = Workspace.all .first(where: { !$0.isVisible && $0.workspaceMonitor.rect.topLeftCorner == point }) { return candidate } return (1 ... Int.max).lazy .map { Workspace.get(byName: String($0)) } .first { $0.isEffectivelyEmpty && !$0.isVisible && !config.persistentWorkspaces.contains($0.name) && $0.forceAssignedMonitor == nil } .orDie("Can't create empty workspace") } final class Workspace: TreeNode, NonLeafTreeNodeObject, Hashable, Comparable { let name: String nonisolated private let nameLogicalSegments: StringLogicalSegments /// `assignedMonitorPoint` must be interpreted only when the workspace is invisible fileprivate var assignedMonitorPoint: CGPoint? = nil @MainActor private init(_ name: String) { self.name = name self.nameLogicalSegments = name.toLogicalSegments() super.init(parent: NilTreeNode.instance, adaptiveWeight: 0, index: 0) } @MainActor static var all: [Workspace] { workspaceNameToWorkspace.values.sorted() } @MainActor static func get(byName name: String) -> Workspace { if let existing = workspaceNameToWorkspace[name] { return existing } else { let workspace = Workspace(name) workspaceNameToWorkspace[name] = workspace return workspace } } nonisolated static func < (lhs: Workspace, rhs: Workspace) -> Bool { lhs.nameLogicalSegments < rhs.nameLogicalSegments } override func getWeight(_ targetOrientation: Orientation) -> CGFloat { workspaceMonitor.visibleRectPaddedByOuterGaps.getDimension(targetOrientation) } override func setWeight(_ targetOrientation: Orientation, _ newValue: CGFloat) { die("It's not possible to change weight of Workspace") } @MainActor var description: String { let description = [ ("name", name), ("isVisible", String(isVisible)), ("isEffectivelyEmpty", String(isEffectivelyEmpty)), ("doKeepAlive", String(config.persistentWorkspaces.contains(name))), ].map { "\($0.0): '\(String(describing: $0.1))'" }.joined(separator: ", ") return "Workspace(\(description))" } @MainActor static func garbageCollectUnusedWorkspaces() { for name in config.persistentWorkspaces { _ = get(byName: name) // Make sure that all persistent workspaces are "cached" } workspaceNameToWorkspace = workspaceNameToWorkspace.filter { (_, workspace: Workspace) in config.persistentWorkspaces.contains(workspace.name) || !workspace.isEffectivelyEmpty || workspace.isVisible || workspace.name == focus.workspace.name } } nonisolated static func == (lhs: Workspace, rhs: Workspace) -> Bool { check((lhs === rhs) == (lhs.name == rhs.name), "lhs: \(lhs) rhs: \(rhs)") return lhs === rhs } nonisolated func hash(into hasher: inout Hasher) { hasher.combine(name) } } extension Workspace { @MainActor var isVisible: Bool { visibleWorkspaceToScreenPoint.keys.contains(self) } @MainActor var workspaceMonitor: Monitor { forceAssignedMonitor ?? visibleWorkspaceToScreenPoint[self]?.monitorApproximation ?? assignedMonitorPoint?.monitorApproximation ?? mainMonitor } } extension Monitor { @MainActor var activeWorkspace: Workspace { if let existing = screenPointToVisibleWorkspace[rect.topLeftCorner] { return existing } // What if monitor configuration changed? (frame.origin is changed) rearrangeWorkspacesOnMonitors() // Normally, recursion should happen only once more because we must take the value from the cache // (Unless, monitor configuration data race happens) return self.activeWorkspace } @MainActor func setActiveWorkspace(_ workspace: Workspace) -> Bool { rect.topLeftCorner.setActiveWorkspace(workspace) } } @MainActor func gcMonitors() { if screenPointToVisibleWorkspace.count != monitors.count { rearrangeWorkspacesOnMonitors() } } extension CGPoint { @MainActor fileprivate func setActiveWorkspace(_ workspace: Workspace) -> Bool { if !isValidAssignment(workspace: workspace, screen: self) { return false } if let prevMonitorPoint = visibleWorkspaceToScreenPoint[workspace] { visibleWorkspaceToScreenPoint.removeValue(forKey: workspace) screenPointToPrevVisibleWorkspace[prevMonitorPoint] = screenPointToVisibleWorkspace.removeValue(forKey: prevMonitorPoint)?.name } if let prevWorkspace = screenPointToVisibleWorkspace[self] { screenPointToPrevVisibleWorkspace[self] = screenPointToVisibleWorkspace.removeValue(forKey: self)?.name visibleWorkspaceToScreenPoint.removeValue(forKey: prevWorkspace) } visibleWorkspaceToScreenPoint[workspace] = self screenPointToVisibleWorkspace[self] = workspace workspace.assignedMonitorPoint = self return true } } @MainActor private func rearrangeWorkspacesOnMonitors() { var oldVisibleScreens: Set = screenPointToVisibleWorkspace.keys.toSet() let newScreens = monitors.map(\.rect.topLeftCorner) var newScreenToOldScreenMapping: [CGPoint: CGPoint] = [:] for newScreen in newScreens { if let oldScreen = oldVisibleScreens.minBy({ ($0 - newScreen).vectorLength }) { check(oldVisibleScreens.remove(oldScreen) != nil) newScreenToOldScreenMapping[newScreen] = oldScreen } } let oldScreenPointToVisibleWorkspace = screenPointToVisibleWorkspace screenPointToVisibleWorkspace = [:] visibleWorkspaceToScreenPoint = [:] for newScreen in newScreens { if let existingVisibleWorkspace = newScreenToOldScreenMapping[newScreen].flatMap({ oldScreenPointToVisibleWorkspace[$0] }), newScreen.setActiveWorkspace(existingVisibleWorkspace) { continue } let stubWorkspace = getStubWorkspace(forPoint: newScreen) check(newScreen.setActiveWorkspace(stubWorkspace), "getStubWorkspace generated incompatible stub workspace (\(stubWorkspace)) for the monitor (\(newScreen)") } } @MainActor private func isValidAssignment(workspace: Workspace, screen: CGPoint) -> Bool { if let forceAssigned = workspace.forceAssignedMonitor, forceAssigned.rect.topLeftCorner != screen { return false } else { return true } } ================================================ FILE: Sources/AppBundle/tree/WorkspaceEx.swift ================================================ import Common extension Workspace { @MainActor var rootTilingContainer: TilingContainer { let containers = children.filterIsInstance(of: TilingContainer.self) switch containers.count { case 0: let orientation: Orientation = switch config.defaultRootContainerOrientation { case .horizontal: .h case .vertical: .v case .auto: workspaceMonitor.then { $0.width >= $0.height } ? .h : .v } return TilingContainer(parent: self, adaptiveWeight: 1, orientation, config.defaultRootContainerLayout, index: INDEX_BIND_LAST) case 1: return containers.singleOrNil().orDie() default: die("Workspace must contain zero or one tiling container as its child") } } var floatingWindows: [Window] { children.filterIsInstance(of: Window.self) } @MainActor var macOsNativeFullscreenWindowsContainer: MacosFullscreenWindowsContainer { let containers = children.filterIsInstance(of: MacosFullscreenWindowsContainer.self) return switch containers.count { case 0: MacosFullscreenWindowsContainer(parent: self) case 1: containers.singleOrNil().orDie() default: dieT("Workspace must contain zero or one MacosFullscreenWindowsContainer") } } @MainActor var macOsNativeHiddenAppsWindowsContainer: MacosHiddenAppsWindowsContainer { let containers = children.filterIsInstance(of: MacosHiddenAppsWindowsContainer.self) return switch containers.count { case 0: MacosHiddenAppsWindowsContainer(parent: self) case 1: containers.singleOrNil().orDie() default: dieT("Workspace must contain zero or one MacosHiddenAppsWindowsContainer") } } @MainActor var forceAssignedMonitor: Monitor? { guard let monitorDescriptions = config.workspaceToMonitorForceAssignment[name] else { return nil } let sortedMonitors = sortedMonitors return monitorDescriptions.lazy .compactMap { $0.resolveMonitor(sortedMonitors: sortedMonitors) } .first } } ================================================ FILE: Sources/AppBundle/tree/frozen/FrozenTreeNode.swift ================================================ import AppKit import Common enum FrozenTreeNode: Sendable { case container(FrozenContainer) case window(FrozenWindow) } struct FrozenContainer: Sendable { let children: [FrozenTreeNode] let layout: Layout let orientation: Orientation let weight: CGFloat @MainActor init(_ container: TilingContainer) { children = container.children.map { switch $0.nodeCases { case .window(let w): .window(FrozenWindow(w)) case .tilingContainer(let c): .container(FrozenContainer(c)) case .workspace, .macosMinimizedWindowsContainer, .macosHiddenAppsWindowsContainer, .macosFullscreenWindowsContainer, .macosPopupWindowsContainer: illegalChildParentRelation(child: $0, parent: container) } } layout = container.layout orientation = container.orientation weight = getWeightOrNil(container) ?? 1 } } struct FrozenWindow: Sendable { let id: UInt32 let weight: CGFloat @MainActor init(_ window: Window) { id = window.windowId weight = getWeightOrNil(window) ?? 1 } } @MainActor private func getWeightOrNil(_ node: TreeNode) -> CGFloat? { ((node.parent as? TilingContainer)?.orientation).map { node.getWeight($0) } } ================================================ FILE: Sources/AppBundle/tree/frozen/FrozenWorld.swift ================================================ struct FrozenWorld { let workspaces: [FrozenWorkspace] let monitors: [FrozenMonitor] let windowIds: Set } @MainActor func collectAllWindowIds(workspace: Workspace) -> [UInt32] { workspace.floatingWindows.map { $0.windowId } + workspace.macOsNativeFullscreenWindowsContainer.children.map { ($0 as! Window).windowId } + workspace.macOsNativeHiddenAppsWindowsContainer.children.map { ($0 as! Window).windowId } + collectAllWindowIdsRecursive(workspace.rootTilingContainer) } func collectAllWindowIdsRecursive(_ node: TreeNode) -> [UInt32] { switch node.nodeCases { case .macosFullscreenWindowsContainer, .macosHiddenAppsWindowsContainer, .macosMinimizedWindowsContainer, .macosPopupWindowsContainer, .workspace: [] case .tilingContainer(let c): c.children.reduce(into: [UInt32]()) { partialResult, elem in partialResult += collectAllWindowIdsRecursive(elem) } case .window(let w): [w.windowId] } } ================================================ FILE: Sources/AppBundle/tree/frozen/closedWindowsCache.swift ================================================ import AppKit /// First line of defence against lock screen /// /// When you lock the screen, all accessibility API becomes unobservable (all attributes become empty, window id /// becomes nil, etc.) which tricks AeroSpace into thinking that all windows were closed. /// That's why every time a window dies AeroSpace caches the "entire world" (unless window is already presented in the cache) /// so that once the screen is unlocked, AeroSpace could restore windows to where they were @MainActor private var closedWindowsCache = FrozenWorld(workspaces: [], monitors: [], windowIds: []) struct FrozenMonitor: Sendable { let topLeftCorner: CGPoint let visibleWorkspace: String @MainActor init(_ monitor: Monitor) { topLeftCorner = monitor.rect.topLeftCorner visibleWorkspace = monitor.activeWorkspace.name } } struct FrozenWorkspace: Sendable { let name: String let monitor: FrozenMonitor // todo drop this property, once monitor to workspace assignment migrates to TreeNode let rootTilingNode: FrozenContainer let floatingWindows: [FrozenWindow] let macosUnconventionalWindows: [FrozenWindow] @MainActor init(_ workspace: Workspace) { name = workspace.name monitor = FrozenMonitor(workspace.workspaceMonitor) rootTilingNode = FrozenContainer(workspace.rootTilingContainer) floatingWindows = workspace.floatingWindows.map(FrozenWindow.init) macosUnconventionalWindows = workspace.macOsNativeHiddenAppsWindowsContainer.children.map { FrozenWindow($0 as! Window) } + workspace.macOsNativeFullscreenWindowsContainer.children.map { FrozenWindow($0 as! Window) } } } @MainActor func cacheClosedWindowIfNeeded() { let allWs = Workspace.all let allWindowIds = allWs.flatMap { collectAllWindowIds(workspace: $0) }.toSet() if allWindowIds.isSubset(of: closedWindowsCache.windowIds) { return // already cached } closedWindowsCache = FrozenWorld( workspaces: allWs.map { FrozenWorkspace($0) }, monitors: monitors.map(FrozenMonitor.init), windowIds: allWindowIds, ) } @MainActor func restoreClosedWindowsCacheIfNeeded(newlyDetectedWindow: Window) async throws -> Bool { if !closedWindowsCache.windowIds.contains(newlyDetectedWindow.windowId) { return false } let monitors = monitors let topLeftCornerToMonitor = monitors.grouped { $0.rect.topLeftCorner } for frozenWorkspace in closedWindowsCache.workspaces { let workspace = Workspace.get(byName: frozenWorkspace.name) _ = topLeftCornerToMonitor[frozenWorkspace.monitor.topLeftCorner]? .singleOrNil()? .setActiveWorkspace(workspace) for frozenWindow in frozenWorkspace.floatingWindows { MacWindow.get(byId: frozenWindow.id)?.bindAsFloatingWindow(to: workspace) } for frozenWindow in frozenWorkspace.macosUnconventionalWindows { // Will get fixed by normalizations MacWindow.get(byId: frozenWindow.id)?.bindAsFloatingWindow(to: workspace) } let prevRoot = workspace.rootTilingContainer // Save prevRoot into a variable to avoid it being garbage collected earlier than needed let potentialOrphans = prevRoot.allLeafWindowsRecursive prevRoot.unbindFromParent() restoreTreeRecursive(frozenContainer: frozenWorkspace.rootTilingNode, parent: workspace, index: INDEX_BIND_LAST) for window in (potentialOrphans - workspace.rootTilingContainer.allLeafWindowsRecursive) { try await window.relayoutWindow(on: workspace, forceTile: true) } } for monitor in closedWindowsCache.monitors { _ = topLeftCornerToMonitor[monitor.topLeftCorner]? .singleOrNil()? .setActiveWorkspace(Workspace.get(byName: monitor.visibleWorkspace)) } return true } @discardableResult @MainActor private func restoreTreeRecursive(frozenContainer: FrozenContainer, parent: NonLeafTreeNodeObject, index: Int) -> Bool { let container = TilingContainer( parent: parent, adaptiveWeight: frozenContainer.weight, frozenContainer.orientation, frozenContainer.layout, index: index, ) for (index, child) in frozenContainer.children.enumerated() { switch child { case .window(let w): // Stop the loop if can't find the window, because otherwise all the subsequent windows will have incorrect index guard let window = MacWindow.get(byId: w.id) else { return false } window.bind(to: container, adaptiveWeight: w.weight, index: index) case .container(let c): // There is no reason to continue if !restoreTreeRecursive(frozenContainer: c, parent: container, index: index) { return false } } } return true } // Consider the following case: // 1. Close window // 2. The previous step lead to caching the whole world // 3. Change something in the layout // 4. Lock the screen // 5. The cache won't be updated because all alive windows are already cached // 6. Unlock the screen // 7. The wrong cache is used // // That's why we have to reset the cache every time layout changes. The layout can only be changed by running commands // and with mouse manipulations @MainActor func resetClosedWindowsCache() { closedWindowsCache = FrozenWorld(workspaces: [], monitors: [], windowIds: []) } ================================================ FILE: Sources/AppBundle/tree/normalizeContainers.swift ================================================ extension Workspace { @MainActor func normalizeContainers() { rootTilingContainer.unbindEmptyAndAutoFlatten() // Beware! rootTilingContainer may change after this line of code if config.enableNormalizationOppositeOrientationForNestedContainers { rootTilingContainer.normalizeOppositeOrientationForNestedContainers() } } } extension TilingContainer { @MainActor fileprivate func unbindEmptyAndAutoFlatten() { if let child = children.singleOrNil(), config.enableNormalizationFlattenContainers && (child is TilingContainer || !isRootContainer) { child.unbindFromParent() let mru = parent?.mostRecentChild let previousBinding = unbindFromParent() child.bind(to: previousBinding.parent, adaptiveWeight: previousBinding.adaptiveWeight, index: previousBinding.index) (child as? TilingContainer)?.unbindEmptyAndAutoFlatten() if mru != self { mru?.markAsMostRecentChild() } else { child.markAsMostRecentChild() } } else { for child in children { (child as? TilingContainer)?.unbindEmptyAndAutoFlatten() } if children.isEmpty && !isRootContainer { unbindFromParent() } } } } ================================================ FILE: Sources/AppBundle/ui/AppearanceTheme.swift ================================================ import SwiftUI import Common enum AppearanceTheme { case light case dark /// System Settings -> Appearance -> Light/Dark /// This is the theme representing how the UI should look inside the app (this might be different than the menu bar color) @MainActor static var current: AppearanceTheme { let name = NSApplication.shared.effectiveAppearance.name let isDarkAppearance = name == .vibrantDark || name == .darkAqua || name == .accessibilityHighContrastDarkAqua || name == .accessibilityHighContrastVibrantDark return isDarkAppearance ? .dark : .light } } ================================================ FILE: Sources/AppBundle/ui/ExperimentalUISettings.swift ================================================ import SwiftUI struct ExperimentalUISettings { var displayStyle: MenuBarStyle { get { if let value = UserDefaults.standard.string(forKey: ExperimentalUISettingsItems.displayStyle.rawValue) { return MenuBarStyle(rawValue: value) ?? .monospacedText } else { return .monospacedText } } set { UserDefaults.standard.setValue(newValue.rawValue, forKey: ExperimentalUISettingsItems.displayStyle.rawValue) UserDefaults.standard.synchronize() } } } enum MenuBarStyle: String, CaseIterable, Identifiable, Equatable, Hashable { case monospacedText case systemText case squares case i3 case i3Ordered var id: String { rawValue } var title: String { switch self { case .monospacedText: "Monospaced font" case .systemText: "System font" case .squares: "Square images" case .i3: "i3 style grouped" case .i3Ordered: "i3 style ordered" } } } enum ExperimentalUISettingsItems: String { case displayStyle } @MainActor func getExperimentalUISettingsMenu(viewModel: TrayMenuModel) -> some View { let color = AppearanceTheme.current == .dark ? Color.white : Color.black return Menu { Text("Menu bar style (macOS 14 or later):") ForEach(MenuBarStyle.allCases, id: \.id) { style in MenuBarStyleButton(style: style, color: color).environmentObject(viewModel) } } label: { Text("Experimental UI Settings (No stability guarantees)") } } @MainActor struct MenuBarStyleButton: View { @EnvironmentObject var viewModel: TrayMenuModel let style: MenuBarStyle let color: Color var body: some View { Button { viewModel.experimentalUISettings.displayStyle = style } label: { Toggle(isOn: .constant(viewModel.experimentalUISettings.displayStyle == style)) { MenuBarLabel(style: style, color: color) .environmentObject(viewModel) Text(" - " + style.title) } } } } ================================================ FILE: Sources/AppBundle/ui/MenuBar.swift ================================================ import Common import Foundation import SwiftUI @MainActor public func menuBar(viewModel: TrayMenuModel) -> some Scene { // todo should it be converted to "SwiftUI struct"? MenuBarExtra { let shortIdentification = "\(aeroSpaceAppName) v\(aeroSpaceAppVersion) \(gitShortHash)" let identification = "\(aeroSpaceAppName) v\(aeroSpaceAppVersion) \(gitHash)" Text(shortIdentification) Button("Copy to clipboard") { identification.copyToClipboard() } .keyboardShortcut("C", modifiers: .command) Divider() if let token: RunSessionGuard = .isServerEnabled { Text("Workspaces:") ForEach(viewModel.workspaces, id: \.name) { workspace in Button { Task { try await runLightSession(.menuBarButton, token) { _ = Workspace.get(byName: workspace.name).focusWorkspace() } } } label: { Toggle(isOn: .constant(workspace.isFocused)) { Text(workspace.name + workspace.suffix).font(.system(.body, design: .monospaced)) } } } Divider() } Button { NSWorkspace.shared.open(URL(string: "https://github.com/sponsors/nikitabobko").orDie()) viewModel.sponsorshipMessage = sponsorshipPrompts.randomElement().orDie() } label: { Text("Sponsor AeroSpace on GitHub") Text(viewModel.sponsorshipMessage) } Divider() Button(viewModel.isEnabled ? "Disable" : "Enable") { Task { try await runLightSession(.menuBarButton, .forceRun) { () throws in _ = try await EnableCommand(args: EnableCmdArgs(rawArgs: [], targetState: .toggle)) .run(.defaultEnv, .emptyStdin) } } }.keyboardShortcut("E", modifiers: .command) getExperimentalUISettingsMenu(viewModel: viewModel) openConfigButton() reloadConfigButton() Button("Quit \(aeroSpaceAppName)") { Task { defer { terminateApp() } try await terminationHandler.beforeTermination() } }.keyboardShortcut("Q", modifiers: .command) } label: { if viewModel.isEnabled { MenuBarLabel().environmentObject(viewModel) } else { Image(systemName: "pause.circle.fill") .resizable() .aspectRatio(contentMode: .fit) } } } @MainActor @ViewBuilder func openConfigButton(showShortcutGroup: Bool = false) -> some View { let editor = getTextEditorToOpenConfig() let button = Button("Open config in '\(editor.lastPathComponent)'") { let fallbackConfig: URL = FileManager.default.homeDirectoryForCurrentUser.appending(path: configDotfileName) switch findCustomConfigUrl() { case .file(let url): url.open(with: editor) case .noCustomConfigExists: _ = try? FileManager.default.copyItem(atPath: defaultConfigUrl.path, toPath: fallbackConfig.path) fallbackConfig.open(with: editor) case .ambiguousConfigError: fallbackConfig.open(with: editor) } }.keyboardShortcut(",", modifiers: .command) if showShortcutGroup { shortcutGroup(label: Text("⌘ ,"), content: button) } else { button } } @MainActor @ViewBuilder func reloadConfigButton(showShortcutGroup: Bool = false) -> some View { if let token: RunSessionGuard = .isServerEnabled { let button = Button("Reload config") { Task { try await runLightSession(.menuBarButton, token) { _ = try await reloadConfig() } } }.keyboardShortcut("R", modifiers: .command) if showShortcutGroup { shortcutGroup(label: Text("⌘ R"), content: button) } else { button } } } func shortcutGroup(label: some View, content: some View) -> some View { GroupBox { VStack(alignment: .trailing, spacing: 6) { label .foregroundStyle(Color.secondary) content } } } func getTextEditorToOpenConfig() -> URL { NSWorkspace.shared.urlForApplication(toOpen: findCustomConfigUrl().urlOrNil ?? defaultConfigUrl)? .takeIf { $0.lastPathComponent != "Xcode.app" } // Blacklist Xcode. It is too heavy to open plain text files ?? URL(filePath: "/System/Applications/TextEdit.app") } ================================================ FILE: Sources/AppBundle/ui/MenuBarLabel.swift ================================================ import Common import Foundation import SwiftUI @MainActor struct MenuBarLabel: View { @Environment(\.colorScheme) var menuColorScheme: ColorScheme @EnvironmentObject var viewModel: TrayMenuModel let color: Color? let style: MenuBarStyle? let hStackSpacing = CGFloat(6) let itemSize = CGFloat(40) let itemBorderSize = CGFloat(3) let itemCornerRadius = CGFloat(6) private var finalColor: Color { return color ?? (menuColorScheme == .dark ? Color.white : Color.black) } init(style: MenuBarStyle? = nil, color: Color? = nil) { self.style = style self.color = color } var body: some View { if #available(macOS 14, *) { // https://github.com/nikitabobko/AeroSpace/issues/1122 let renderer = ImageRenderer(content: menuBarContent) if let cgImage = renderer.cgImage { // Using scale: 1 results in a blurry image for unknown reasons Image(cgImage, scale: 2, label: Text(viewModel.trayText)) } else { // In case image can't be rendered fallback to plain text Text(viewModel.trayText) } } else { // macOS 13 and lower Text(viewModel.trayText) } } var menuBarContent: some View { return HStack(spacing: hStackSpacing) { let style = style ?? viewModel.experimentalUISettings.displayStyle switch style { case .monospacedText: getText(for: .monospaced) case .systemText: getText(for: .default) case .squares: squares case .i3: squares let workspaces = viewModel.workspaces.filter { !$0.isEffectivelyEmpty && !$0.isVisible } if !workspaces.isEmpty { otherWorkspaces(with: workspaces) } case .i3Ordered: let modeItem = viewModel.trayItems.first { $0.type == .mode } if let modeItem { itemView(for: modeItem) modeSeparator(with: .monospaced) } let orderedWorkspaces = viewModel.workspaces.filter { !$0.isEffectivelyEmpty || $0.isVisible } ForEach(orderedWorkspaces, id: \.name) { item in let trayItem = TrayItem( type: .workspace, name: item.name, isActive: item.isFocused, hasFullscreenWindows: item.hasFullscreenWindows, ) itemView(for: trayItem) .opacity(item.isVisible ? 1 : 0.5) } } } } private func getText(for design: Font.Design) -> some View { Text(viewModel.trayText) .font(.system(.largeTitle, design: design)) .foregroundStyle(finalColor) } private var squares: some View { ForEach(viewModel.trayItems, id: \.id) { item in itemView(for: item) if item.type == .mode { modeSeparator(with: .monospaced) } } } private func otherWorkspaces(with otherWorkspaces: [WorkspaceViewModel]) -> some View { Group { Text("|") .font(.system(.largeTitle)) .foregroundStyle(finalColor) .bold() .padding(.bottom, 6) ForEach(otherWorkspaces, id: \.name) { item in itemView(for: TrayItem(type: .workspace, name: item.name, isActive: false, hasFullscreenWindows: item.hasFullscreenWindows)) } } .opacity(0.6) } private func modeSeparator(with design: Font.Design) -> some View { Text(":") .font(.system(.largeTitle, design: design)) .foregroundStyle(finalColor) .bold() } @ViewBuilder fileprivate func itemView(for item: TrayItem) -> some View { let view = itemSubView(for: item) if item.hasFullscreenWindows { let strokeStyle = StrokeStyle(lineWidth: 2, lineCap: .square, lineJoin: .miter, miterLimit: 10, dash: [10, 5], dashPhase: 3) view .padding(4) .overlay { RoundedRectangle(cornerRadius: itemCornerRadius, style: .continuous) .strokeBorder(finalColor, style: strokeStyle) } } else { view } } @ViewBuilder fileprivate func itemSubView(for item: TrayItem) -> some View { // If workspace name contains emojis we use the plain emoji in text to avoid visibility issues scaling the emoji to fit the squares if item.name.containsEmoji() { Text(item.name) .font(.system(.largeTitle)) .foregroundStyle(finalColor) .frame(height: itemSize) } else { if let imageName = item.systemImageName { Image(systemName: imageName) .resizable() .aspectRatio(contentMode: .fit) .symbolRenderingMode(.monochrome) .foregroundStyle(finalColor) .frame(width: itemSize, height: itemSize) } else { let text = Text(item.name) .font(.system(.largeTitle)) .bold() .padding(.horizontal, itemBorderSize * 2) .frame(height: itemSize) if item.isActive { ZStack { text.background { RoundedRectangle(cornerRadius: itemCornerRadius, style: .circular) } text.blendMode(.destinationOut) } .compositingGroup() .foregroundStyle(finalColor) .frame(height: itemSize) } else { text.background { RoundedRectangle(cornerRadius: itemCornerRadius, style: .continuous) .strokeBorder(lineWidth: itemBorderSize) } .foregroundStyle(finalColor) .frame(height: itemSize) } } } } } extension String { fileprivate func containsEmoji() -> Bool { unicodeScalars.contains { $0.properties.isEmoji && $0.properties.isEmojiPresentation } } } ================================================ FILE: Sources/AppBundle/ui/MessageView.swift ================================================ import Common import SwiftUI @MainActor public func getMessageWindow(messageModel: MessageModel) -> some Scene { // Using SwiftUI.Window because another class in AeroSpace is already called Window SwiftUI.Window(messageModel.message?.title ?? aeroSpaceAppName, id: messageWindowId) { MessageView(model: messageModel) .onAppear { // Set activation policy; otherwise, AeroSpace windows won't be able to receive focus and accept keyboard input NSApp.setActivationPolicy(.accessory) NSApplication.shared.windows.forEach { if $0.identifier?.rawValue == messageWindowId { $0.level = .floating $0.styleMask.remove(.miniaturizable) // Disable minimize button, because we don't unminimize the window on config error } } } // .windowMinimizeBehavior(WindowInteractionBehavior.disabled) // SwiftUI way of hiding minimize button. Available only since macOS 15 } .windowResizability(.contentMinSize) //.windowLevel(.floating) //This might be the SwiftUI way of doing window level instead of the onAppear block above, but it's only available from macOS 15.0 } public let messageWindowId = "\(aeroSpaceAppName).messageView" struct MessageView: View { @StateObject private var model: MessageModel @Environment(\.dismiss) private var dismiss: DismissAction @FocusState var focus: Bool init(model: MessageModel) { self._model = .init(wrappedValue: model) } public var body: some View { VStack(alignment: .leading) { HStack(alignment: .center) { Image(systemName: "exclamationmark.triangle.fill") .foregroundColor(.yellow) .font(.system(size: 48)) Text("\(model.message?.description ?? "")") .padding(.horizontal) .focusable() } .padding() ScrollView { VStack(alignment: .leading) { HStack { let cancelOnEnterBinding: Binding = Binding( get: { model.message?.body ?? "" }, set: { newText in if let prev = model.message?.body.count(where: \.isNewline), newText.count(where: \.isNewline) > prev { model.message = nil } }, ) TextEditor(text: cancelOnEnterBinding) .font(.system(size: 12).monospaced()) .focused($focus) // .onKeyPress(.return) { return .handled } // enter handling alternative. Only available since macOS 14 Spacer() } Spacer() } .padding() } .background(Color(.controlBackgroundColor)) .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) .padding(.horizontal) HStack { Spacer() if let type = model.message?.type { switch type { case .config: reloadConfigButton(showShortcutGroup: true) openConfigButton(showShortcutGroup: true) } } let closeButton = Button("Close") { model.message = nil }.keyboardShortcut(.defaultAction) shortcutGroup(label: Image(systemName: "return.left"), content: closeButton) } .padding() } .textSelection(.enabled) .frame(minWidth: 480, maxWidth: 960, minHeight: 200) .onChange(of: model.message) { message in if message == nil { self.dismiss() } } .onDisappear { // If user closes the screen with the macOS native close (x) button and then the error is still the same, this window will not appear again model.message = nil } .onAppear { focus = true } } } public final class MessageModel: ObservableObject { @MainActor public static let shared = MessageModel() @Published public var message: Message? = nil private init() {} } public enum MessageType { case config } public struct Message: Hashable, Equatable { public let type: MessageType public let title: String public let description: String public let body: String init(type: MessageType = .config, title: String = aeroSpaceAppName, description: String, body: String) { self.type = type self.title = title self.description = description self.body = body } } ================================================ FILE: Sources/AppBundle/ui/NSPanelHud.swift ================================================ import AppKit open class NSPanelHud: NSPanel { init() { super.init( contentRect: .zero, styleMask: [.nonactivatingPanel, .borderless, .hudWindow, .utilityWindow], backing: .buffered, defer: false, ) self.level = .floating self.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] self.isReleasedWhenClosed = false self.hidesOnDeactivate = false self.isMovableByWindowBackground = false self.alphaValue = 1 self.hasShadow = true self.backgroundColor = .clear } } ================================================ FILE: Sources/AppBundle/ui/SecureInputView.swift ================================================ import AppKit import Carbon import SwiftUI private let iconSize = CGSize(width: 50, height: 50) private let textSize = CGSize(width: 440, height: 110) public final class SecureInputPanel: NSPanelHud { @MainActor public static var shared: SecureInputPanel = SecureInputPanel() private var hostingView = NSHostingView(rootView: SecureInputView()) override private init() { super.init() } @MainActor public func refresh() { if let activeMode, TrayMenuModel.shared.isEnabled && config.modes[activeMode]?.bindings.isEmpty == false && IsSecureEventInputEnabled() { if isVisible { return } self.contentView?.subviews.removeAll() hostingView = NSHostingView(rootView: SecureInputView()) hostingView.frame = NSRect(x: 0, y: 0, width: iconSize.width, height: iconSize.height) self.contentView?.addSubview(hostingView) let x = mainMonitor.width - iconSize.width - 20 let panelFrame = NSRect(x: x, y: 20, width: iconSize.width, height: iconSize.width) self.setFrame(panelFrame, display: true) self.orderFrontRegardless() } else { close() } } public func updateFrame(isMinimized: Bool) { let width = isMinimized ? iconSize.width : textSize.width let height = isMinimized ? iconSize.height : textSize.height hostingView.frame = NSRect(x: 0, y: 0, width: width, height: height) let x = mainMonitor.width - width - 20 let panelFrame = NSRect(x: x, y: 20, width: width, height: height) self.setFrame(panelFrame, display: true) } } struct SecureInputView: View { @State var isMinimized: Bool = true @Environment(\.colorScheme) var colorScheme: ColorScheme private var fontColor: Color { colorScheme == .dark ? Color.black : Color.white } var body: some View { ZStack(alignment: .center) { Rectangle() .fill(Color.gray.opacity(isMinimized ? 0.8 : 1.0)) if isMinimized { Image(systemName: "lock.shield.fill") .resizable() .aspectRatio(contentMode: .fit) .padding(6) } else { Text("AeroSpace cannot respond to keyboard shortcuts while **Secure Input** is active. **Secure Input** is a macOS security feature that prevents applications from reading keyboard events.") .font(.title3) .padding(10) } } .foregroundStyle(fontColor) .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) .onTapGesture { if !isMinimized { SecureInputPanel.shared.refresh() } isMinimized.toggle() SecureInputPanel.shared.updateFrame(isMinimized: isMinimized) } .frame( width: isMinimized ? iconSize.width : textSize.width, height: isMinimized ? iconSize.height : textSize.height, ) } } ================================================ FILE: Sources/AppBundle/ui/TrayMenuModel.swift ================================================ import AppKit import Common public final class TrayMenuModel: ObservableObject { @MainActor public static let shared = TrayMenuModel() private init() {} @Published var trayText: String = "" @Published var trayItems: [TrayItem] = [] /// Is "layouting" enabled @Published var isEnabled: Bool = true @Published var workspaces: [WorkspaceViewModel] = [] @Published var experimentalUISettings: ExperimentalUISettings = ExperimentalUISettings() @Published var sponsorshipMessage: String = sponsorshipPrompts.randomElement().orDie() } @MainActor func updateTrayText() { let sortedMonitors = sortedMonitors let focus = focus TrayMenuModel.shared.trayText = (activeMode?.takeIf { $0 != mainModeId }?.first.map { "(\($0.uppercased())) " } ?? "") + sortedMonitors .map { let hasFullscreenWindows = $0.activeWorkspace.allLeafWindowsRecursive.contains { $0.isFullscreen } let activeWorkspaceName = hasFullscreenWindows ? "[\($0.activeWorkspace.name)]" : $0.activeWorkspace.name return ($0.activeWorkspace == focus.workspace && sortedMonitors.count > 1 ? "*" : "") + activeWorkspaceName } .joined(separator: " │ ") TrayMenuModel.shared.workspaces = Workspace.all.map { let apps = $0.allLeafWindowsRecursive.map { $0.app.name?.takeIf { !$0.isEmpty } }.filterNotNil().toSet() let dash = " - " let suffix = switch true { case !apps.isEmpty: dash + apps.sorted().joinTruncating(separator: ", ", length: 25) case $0.isVisible: dash + $0.workspaceMonitor.name default: "" } let hasFullscreenWindows = $0.allLeafWindowsRecursive.contains { $0.isFullscreen } return WorkspaceViewModel( name: $0.name, suffix: suffix, isFocused: focus.workspace == $0, isEffectivelyEmpty: $0.isEffectivelyEmpty, isVisible: $0.isVisible, hasFullscreenWindows: hasFullscreenWindows, ) } var items = sortedMonitors.map { let hasFullscreenWindows = $0.activeWorkspace.allLeafWindowsRecursive.contains { $0.isFullscreen } return TrayItem( type: .workspace, name: $0.activeWorkspace.name, isActive: $0.activeWorkspace == focus.workspace, hasFullscreenWindows: hasFullscreenWindows, ) } let mode = activeMode?.takeIf { $0 != mainModeId }?.first.map { TrayItem(type: .mode, name: $0.uppercased(), isActive: true, hasFullscreenWindows: false) } if let mode { items.insert(mode, at: 0) } TrayMenuModel.shared.trayItems = items } struct WorkspaceViewModel: Hashable { let name: String let suffix: String let isFocused: Bool let isEffectivelyEmpty: Bool let isVisible: Bool let hasFullscreenWindows: Bool } enum TrayItemType: String, Hashable { case mode case workspace } private let validLetters = "A" ... "Z" struct TrayItem: Hashable, Identifiable { let type: TrayItemType let name: String let isActive: Bool let hasFullscreenWindows: Bool var systemImageName: String? { // System image type is only valid for numbers 0 to 50 and single capital char workspace name if let number = Int(name) { if !(0 ... 50).contains(number) { return nil } } else if name.count == 1 { if !validLetters.contains(name) { return nil } } else { return nil } let lowercasedName = name.lowercased() switch type { case .mode: return "\(lowercasedName).circle" case .workspace: if isActive { return "\(lowercasedName).square.fill" } else { return "\(lowercasedName).square" } } } var id: String { return type.rawValue + name } } ================================================ FILE: Sources/AppBundle/ui/VolumeView.swift ================================================ import AppKit import SwiftUI public final class VolumePanel: NSPanelHud { @MainActor public static var shared: VolumePanel = VolumePanel() private var timer: Timer? private var panelFrame = NSRect(x: 0, y: 0, width: 50, height: 206) override private init() { super.init() } public func update(with volume: Float) { timer?.invalidate() self.contentView?.subviews.removeAll() let hostingView = NSHostingView(rootView: VolumeView(volume: volume)) hostingView.frame = NSRect(x: 0, y: 0, width: panelFrame.width, height: panelFrame.height) self.contentView?.addSubview(hostingView) panelFrame.origin.x = mainMonitor.width - panelFrame.size.width - 20 panelFrame.origin.y = (mainMonitor.height - panelFrame.size.height) / 2 self.setFrame(panelFrame, display: true) self.orderFrontRegardless() startTimer() } func startTimer() { timer = .scheduledTimer(withTimeInterval: 2 /* seconds */, repeats: false) { _ in Task { @MainActor [weak self] in self?.close() } } } } struct VolumeView: View { @State var volume: Float? = nil @Environment(\.colorScheme) var colorScheme: ColorScheme private var barColor: Color { colorScheme == .dark ? Color.white : Color.black } private var fontColor: Color { colorScheme == .dark ? Color.black : Color.white } private var speakerImage: String { guard let volume else { return "speaker.fill" } switch volume { case 0.00 ..< 0.01: return "speaker.slash.fill" case 0.01 ..< 0.25: return "speaker.fill" case 0.25 ..< 0.50: return "speaker.1.fill" case 0.50 ..< 0.75: return "speaker.2.fill" default: return "speaker.3.fill" } } private let bar = CGSize(width: 44, height: 200) var body: some View { ZStack(alignment: .bottom) { if let volume { Rectangle() .fill(Color.gray.opacity(0.8)) Rectangle() .fill(barColor) .frame(height: CGFloat(volume) * bar.height) VStack { Text("\(Int(volume * 100))%") .font(.system(size: 12, weight: .bold)) Image(systemName: speakerImage) .frame(width: 30, height: 30, alignment: .center) .padding(.bottom, 10) } .foregroundStyle(fontColor) } } .frame(width: bar.width, height: bar.height) .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) } } ================================================ FILE: Sources/AppBundle/util/ArrayEx.swift ================================================ import Common extension Array { func singleOrNil(where predicate: (Self.Element) throws -> Bool) rethrows -> Self.Element? { var found: Self.Element? = nil for elem in self where try predicate(elem) { if found == nil { found = elem } else { return nil } } return found } } extension Array where Self.Element: Equatable { @discardableResult mutating func remove(element: Self.Element) -> Int? { if let index = firstIndex(of: element) { remove(at: index) return index } else { return nil } } } func - (lhs: [T], rhs: [T]) -> [T] where T: Hashable { let r = rhs.toSet() return lhs.filter { !r.contains($0) } } ================================================ FILE: Sources/AppBundle/util/AwaitableOneTimeBroadcastLatch.swift ================================================ import Common import Foundation actor AwaitableOneTimeBroadcastLatch { private var done = false private var awaiters: [UniqueToken: Nullable>] = [:] func await() async throws { try checkCancellation() if done { return } let id = UniqueToken() try await withTaskCancellationHandler { try await withCheckedThrowingContinuation { (cont: CheckedContinuation<(), any Error>) in if let awaiter = awaiters.removeValue(forKey: id) { check(awaiter.isNull) cont.resume(throwing: CancellationError()) } else if done { cont.resume() } else { awaiters[id] = .just(cont) } } } onCancel: { Task { await cancel(id: id) } } } private func cancel(id: UniqueToken) { if let awaiter = awaiters.removeValue(forKey: id) { awaiter.valueOrNil.orDie().resume(throwing: CancellationError()) } else if !done { awaiters[id] = .null // Indicate to 'await' that the client should be cancelled right away when it suspends } } func signalToAll() { done = true for (_, awaiter) in awaiters { awaiter.valueOrNil?.resume() } awaiters = [:] } } ================================================ FILE: Sources/AppBundle/util/AxSubscription.swift ================================================ import AppKit import Common /// The subscription is active as long as you keep this class in memory final class AxSubscription { let obs: AXObserver let ax: AXUIElement let axThreadToken: AxAppThreadToken = axTaskLocalAppThreadToken ?? dieT("axTaskLocalAppThreadToken is not initialized") var notifKeys: Set = [] private init(obs: AXObserver, ax: AXUIElement) { axThreadToken.checkEquals(axTaskLocalAppThreadToken) self.obs = obs self.ax = ax } private func subscribe(_ key: String) throws -> Bool { axThreadToken.checkEquals(axTaskLocalAppThreadToken) if AXObserverAddNotification(obs, ax, key as CFString, nil) == .success { notifKeys.insert(key) return true } else { return false } } static func bulkSubscribe(_ nsApp: NSRunningApplication, _ ax: AXUIElement, _ job: RunLoopJob, _ handlerToNotifKeyMapping: HandlerToNotifKeyMapping) throws -> [AxSubscription] { var result: [AxSubscription] = [] var visitedNotifKeys: Set = [] for (handler, notifKeys) in handlerToNotifKeyMapping { try job.checkCancellation() guard let obs = AXObserver.new(nsApp.processIdentifier, handler) else { return [] } let subscription = AxSubscription(obs: obs, ax: ax) for key: String in notifKeys { try job.checkCancellation() assert(visitedNotifKeys.insert(key).inserted) if try !subscription.subscribe(key) { return [] } } CFRunLoopAddSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(obs), .defaultMode) result.append(subscription) } return result } deinit { axThreadToken.checkEquals(axTaskLocalAppThreadToken) CFRunLoopRemoveSource(CFRunLoopGetCurrent(), AXObserverGetRunLoopSource(obs), .defaultMode) for notifKey in notifKeys { AXObserverRemoveNotification(obs, ax, notifKey as CFString) } } } typealias HandlerToNotifKeyMapping = [(AXObserverCallback, [String])] ================================================ FILE: Sources/AppBundle/util/AxUiElementMock.swift ================================================ import AppKit import Common /// Alternative name: AttrAddressibleStorage protocol AxUiElementMock { func get(_ attr: Attr) -> Attr.T? func containingWindowId() -> CGWindowID? } extension AxUiElementMock { var cast: AXUIElement { self as! AXUIElement } } ================================================ FILE: Sources/AppBundle/util/LazySequenceProtocolEx.swift ================================================ // periphery:ignore extension LazySequenceProtocol { func filterNotNil() -> LazyMapSequence.Elements, Unwrapped> where Element == Unwrapped? { filter { $0 != nil }.map { $0.orDie() } } } ================================================ FILE: Sources/AppBundle/util/MruStack.swift ================================================ /// Stack with most recently element on top final class MruStack: Sequence { typealias Element = T private var mruNode: Node? = nil func makeIterator() -> MruStackIterator { MruStackIterator(mruNode) } var mostRecent: T? { mruNode?.value } func pushOrRaise(_ value: T) { remove(value) mruNode = Node(value, mruNode) } @discardableResult func remove(_ value: T) -> Bool { var prev: Node? = nil var current = mruNode while let cur = current { if cur.value == value { if let prev { prev.next = cur.next } else { mruNode = current?.next } cur.next = nil return true } prev = cur current = cur.next } return false } } struct MruStackIterator: IteratorProtocol { typealias Element = T private var current: Node? fileprivate init(_ current: Node?) { self.current = current } mutating func next() -> T? { let result = current?.value current = current?.next return result } } private final class Node { var next: Node? = nil let value: T init(_ value: T, _ next: Node?) { self.value = value self.next = next } init(_ value: T) { self.value = value } } ================================================ FILE: Sources/AppBundle/util/NSRunningApplicationEx.swift ================================================ import AppKit extension NSRunningApplication { var idForDebug: String { "PID: \(processIdentifier) ID: \(bundleIdentifier ?? executableURL?.description ?? "")" } } ================================================ FILE: Sources/AppBundle/util/NsApplicationEx.swift ================================================ import AppKit extension NSApplication.ActivationPolicy { var prettyDescription: String { switch self { case .accessory: "accessory" case .prohibited: " prohibited" case .regular: "regular" @unknown default: "unknown \(self.rawValue)" } } } ================================================ FILE: Sources/AppBundle/util/SetEx.swift ================================================ // periphery:ignore extension Set { func toArray() -> [Element] { Array(self) } @inlinable static func += (lhs: inout Set, rhs: any Sequence) { lhs.formUnion(rhs) } @inlinable static func -= (lhs: inout Set, rhs: any Sequence) { lhs.subtract(rhs) } } ================================================ FILE: Sources/AppBundle/util/ThreadGuardedValue.swift ================================================ import Common final class ThreadGuardedValue: Sendable { nonisolated(unsafe) private var _threadGuarded: Value? private let threadToken: AxAppThreadToken = axTaskLocalAppThreadToken ?? dieT("axTaskLocalAppThreadToken is not initialized") init(_ value: Value) { self._threadGuarded = value } var threadGuarded: Value { get { threadToken.checkEquals(axTaskLocalAppThreadToken) return _threadGuarded ?? dieT("Value is already destroyed") } set(newValue) { threadToken.checkEquals(axTaskLocalAppThreadToken) _threadGuarded = newValue } } func destroy() { threadToken.checkEquals(axTaskLocalAppThreadToken) _threadGuarded = nil } deinit { check(_threadGuarded == nil, "The Value must be explicitly destroyed on the appropriate thread before deinit") } } ================================================ FILE: Sources/AppBundle/util/UniqueToken.swift ================================================ final class UniqueToken: Equatable, Hashable, CustomStringConvertible, Sendable { private let hash = Int.random(in: Int.min ... Int.max) static func == (lhs: UniqueToken, rhs: UniqueToken) -> Bool { lhs === rhs } func hash(into hasher: inout Hasher) { hasher.combine(hash) } var description: String { "UniqueToken(\(hash))" } } ================================================ FILE: Sources/AppBundle/util/accessibility.swift ================================================ import AppKit import Common import PrivateApi @MainActor func checkAccessibilityPermissions() { let options = [axTrustedCheckOptionPrompt: true] if !AXIsProcessTrustedWithOptions(options as CFDictionary) { resetAccessibility() // Because macOS doesn't reset it for us when the app signature changes... terminateApp() } } private func resetAccessibility() { _ = try? Process.run(URL(filePath: "/usr/bin/tccutil"), arguments: ["reset", "Accessibility", aeroSpaceAppId]) } protocol ReadableAttr: Sendable { associatedtype T var getter: @Sendable (AnyObject) -> T? { get } var key: String { get } } protocol WritableAttr: ReadableAttr, Sendable { var setter: @Sendable (T) -> CFTypeRef? { get } } // Quick reference: // // // informational attributes // kAXRoleAttribute // kAXSubroleAttribute // kAXRoleDescriptionAttribute // kAXTitleAttribute // kAXDescriptionAttribute // kAXHelpAttribute // // // hierarchy or relationship attributes // kAXParentAttribute // kAXChildrenAttribute // kAXSelectedChildrenAttribute // kAXVisibleChildrenAttribute // kAXWindowAttribute // kAXTopLevelUIElementAttribute // kAXTitleUIElementAttribute // kAXServesAsTitleForUIElementsAttribute // kAXLinkedUIElementsAttribute // kAXSharedFocusElementsAttribute // // // visual state attributes // kAXEnabledAttribute // kAXFocusedAttribute // kAXPositionAttribute // kAXSizeAttribute // // // value attributes // kAXValueAttribute // kAXValueDescriptionAttribute // kAXMinValueAttribute // kAXMaxValueAttribute // kAXValueIncrementAttribute // kAXValueWrapsAttribute // kAXAllowedValuesAttribute // // // text-specific attributes // kAXSelectedTextAttribute // kAXSelectedTextRangeAttribute // kAXSelectedTextRangesAttribute // kAXVisibleCharacterRangeAttribute // kAXNumberOfCharactersAttribute // kAXSharedTextUIElementsAttribute // kAXSharedCharacterRangeAttribute // // // window, sheet, or drawer-specific attributes // kAXMainAttribute // kAXMinimizedAttribute // kAXCloseButtonAttribute // kAXZoomButtonAttribute // kAXMinimizeButtonAttribute // kAXToolbarButtonAttribute // kAXProxyAttribute // kAXGrowAreaAttribute // kAXModalAttribute // kAXDefaultButtonAttribute // kAXCancelButtonAttribute // // // menu or menu item-specific attributes // kAXMenuItemCmdCharAttribute // kAXMenuItemCmdVirtualKeyAttribute // kAXMenuItemCmdGlyphAttribute // kAXMenuItemCmdModifiersAttribute // kAXMenuItemMarkCharAttribute // kAXMenuItemPrimaryUIElementAttribute // // // application element-specific attributes // kAXMenuBarAttribute // kAXWindowsAttribute // kAXFrontmostAttribute // kAXHiddenAttribute // kAXMainWindowAttribute // kAXFocusedWindowAttribute // kAXFocusedUIElementAttribute // kAXExtrasMenuBarAttribute // // // date/time-specific attributes // kAXHourFieldAttribute // kAXMinuteFieldAttribute // kAXSecondFieldAttribute // kAXAMPMFieldAttribute // kAXDayFieldAttribute // kAXMonthFieldAttribute // kAXYearFieldAttribute // // // table, outline, or browser-specific attributes // kAXRowsAttribute // kAXVisibleRowsAttribute // kAXSelectedRowsAttribute // kAXColumnsAttribute // kAXVisibleColumnsAttribute // kAXSelectedColumnsAttribute // kAXSortDirectionAttribute // kAXColumnHeaderUIElementsAttribute // kAXIndexAttribute // kAXDisclosingAttribute // kAXDisclosedRowsAttribute // kAXDisclosedByRowAttribute // // // matte-specific attributes // kAXMatteHoleAttribute // kAXMatteContentUIElementAttribute // // // ruler-specific attributes // kAXMarkerUIElementsAttribute // kAXUnitsAttribute // kAXUnitDescriptionAttribute // kAXMarkerTypeAttribute // kAXMarkerTypeDescriptionAttribute // // // miscellaneous or role-specific attributes // kAXHorizontalScrollBarAttribute // kAXVerticalScrollBarAttribute // kAXOrientationAttribute // kAXHeaderAttribute // kAXEditedAttribute // kAXTabsAttribute // kAXOverflowButtonAttribute // kAXFilenameAttribute // kAXExpandedAttribute // kAXSelectedAttribute // kAXSplittersAttribute // kAXContentsAttribute // kAXNextContentsAttribute // kAXPreviousContentsAttribute // kAXDocumentAttribute // kAXIncrementorAttribute // kAXDecrementButtonAttribute // kAXIncrementButtonAttribute // kAXColumnTitleAttribute // kAXURLAttribute // kAXLabelUIElementsAttribute // kAXLabelValueAttribute // kAXShownMenuUIElementAttribute // kAXIsApplicationRunningAttribute // kAXFocusedApplicationAttribute // kAXElementBusyAttribute // kAXAlternateUIVisibleAttribute enum Ax { struct ReadableAttrImpl: ReadableAttr { var key: String var getter: @Sendable (AnyObject) -> T? } struct WritableAttrImpl: WritableAttr { var key: String var getter: @Sendable (AnyObject) -> T? var setter: @Sendable (T) -> CFTypeRef? } static let titleAttr = WritableAttrImpl( key: kAXTitleAttribute, getter: { $0 as? String }, setter: { $0 as CFTypeRef }, ) static let roleAttr = WritableAttrImpl( key: kAXRoleAttribute, getter: { $0 as? String }, setter: { $0 as CFTypeRef }, ) static let subroleAttr = WritableAttrImpl( key: kAXSubroleAttribute, getter: { $0 as? String }, setter: { $0 as CFTypeRef }, ) static let identifierAttr = ReadableAttrImpl( key: kAXIdentifierAttribute, getter: { $0 as? String }, ) // static let modalAttr = ReadableAttrImpl( // key: kAXModalAttribute, // getter: { $0 as? Bool }, // ) static let enabledAttr = ReadableAttrImpl( key: kAXEnabledAttribute, getter: { $0 as? Bool }, ) static let enhancedUserInterfaceAttr = WritableAttrImpl( key: "AXEnhancedUserInterface", getter: { $0 as? Bool }, setter: { $0 as CFTypeRef }, ) static let minimizedAttr = WritableAttrImpl( key: kAXMinimizedAttribute, getter: { $0 as? Bool }, setter: { $0 as CFTypeRef }, ) //static let minimizedAttr = ReadableAttrImpl( // key: kAXMinimizedAttribute, // getter: { $0 as? Bool } //) static let isFullscreenAttr = WritableAttrImpl( key: "AXFullScreen", getter: { $0 as? Bool }, setter: { $0 as CFTypeRef }, ) static let isFocused = ReadableAttrImpl( key: kAXFocusedAttribute, getter: { $0 as? Bool }, ) static let isMainAttr = WritableAttrImpl( key: kAXMainAttribute, getter: { $0 as? Bool }, setter: { $0 as CFTypeRef }, ) static let sizeAttr = WritableAttrImpl( key: kAXSizeAttribute, getter: { var raw: CGSize = .zero check(AXValueGetValue($0 as! AXValue, .cgSize, &raw)) return raw }, setter: { var size = $0 return AXValueCreate(.cgSize, &size) as CFTypeRef }, ) static let topLeftCornerAttr = WritableAttrImpl( key: kAXPositionAttribute, getter: { var raw: CGPoint = .zero AXValueGetValue($0 as! AXValue, .cgPoint, &raw) return raw }, setter: { var size = $0 return AXValueCreate(.cgPoint, &size) as CFTypeRef }, ) /// Returns windows visible on all monitors /// If some windows are located on not active macOS Spaces then they won't be returned static let windowsAttr = ReadableAttrImpl<[WindowIdAndAxUiElement]>( key: kAXWindowsAttribute, getter: { ($0 as? NSArray)?.compactMap(windowOrNil).map { ($0.windowId, $0.ax.cast) } ?? [] }, ) static let focusedWindowAttr = ReadableAttrImpl( key: kAXFocusedWindowAttribute, getter: windowOrNil, ) //static let mainWindowAttr = ReadableAttrImpl( // key: kAXMainWindowAttribute, // getter: tryGetWindow //) static let closeButtonAttr = ReadableAttrImpl( key: kAXCloseButtonAttribute, getter: castToAxUiElementMock, ) // Note! fullscreen is not the same as "zoom" (green plus) static let fullscreenButtonAttr = ReadableAttrImpl( key: kAXFullScreenButtonAttribute, getter: castToAxUiElementMock, ) // green plus static let zoomButtonAttr = ReadableAttrImpl( key: kAXZoomButtonAttribute, getter: castToAxUiElementMock, ) static let minimizeButtonAttr = ReadableAttrImpl( key: kAXMinimizeButtonAttribute, getter: castToAxUiElementMock, ) //static let growAreaAttr = ReadableAttrImpl( // key: kAXGrowAreaAttribute, // getter: { ($0 as! AXUIElement) } //) } let kAXAeroSynthetic = "Aero.synthetic" private func castToAxUiElementMock(_ a: AnyObject) -> AxUiElementMock { if isUnitTest { if let str = a as? String, let commaIndex = str.firstIndex(of: ",") { let windowId = UInt32.init(String(str.prefix(upTo: commaIndex)).removePrefix("AXUIElement(AxWindowId=")) if let windowId { return castToAxUiElementMock([ "Aero.axWindowId": Json.uint32(windowId), kAXAeroSynthetic: Json.bool(true), ] as AnyObject) } } if let dict = a as? [String: Json] { // Convert from _SwiftDeferredNSDictionary return dict as? AxUiElementMock ?? dieT("Cannot cast \(type(of: a)) to AxUiElementMock") } die("Can't convert \(a) to AxUiElementMock") } return a as! AXUIElement } typealias WindowIdAndAxUiElement = (windowId: UInt32, ax: AXUIElement) typealias WindowIdAndAxUiElementMock = (windowId: UInt32, ax: AxUiElementMock) private func windowOrNil(_ any: Any?) -> WindowIdAndAxUiElementMock? { guard let any else { return nil } let potentialWindow = castToAxUiElementMock(any as AnyObject) // Filter out non-window objects (e.g. Finder's desktop) let windowId = potentialWindow.containingWindowId() if let windowId { return (windowId, potentialWindow) } else { return nil } } extension AXUIElement: AxUiElementMock { func get(_ attr: Attr) -> Attr.T? { let state = signposter.beginInterval(#function, "attr: \(attr.key) axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken?.idForDebug)") defer { signposter.endInterval(#function, state) } var raw: AnyObject? return AXUIElementCopyAttributeValue(self, attr.key as CFString, &raw) == .success ? raw.flatMap(attr.getter) : nil } @discardableResult func set(_ attr: Attr, _ value: Attr.T) -> Bool { if serverArgs.isReadOnly { return false } let state = signposter.beginInterval(#function, "attr: \(attr.key) axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken?.idForDebug)") defer { signposter.endInterval(#function, state) } guard let value = attr.setter(value) else { return false } return AXUIElementSetAttributeValue(self, attr.key as CFString, value) == .success } func containingWindowId() -> CGWindowID? { let state = signposter.beginInterval(#function, "axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken?.idForDebug)") defer { signposter.endInterval(#function, state) } var cgWindowId = CGWindowID() return _AXUIElementGetWindow(self, &cgWindowId) == .success ? cgWindowId : nil } } extension AXObserver { static func new(_ pid: pid_t, _ handler: AXObserverCallback) -> AXObserver? { var observer: AXObserver? = nil return AXObserverCreate(pid, handler, &observer) == .success ? observer : nil } } ================================================ FILE: Sources/AppBundle/util/appBundleUtil.swift ================================================ import AppKit import Common import Foundation import os let signposter = OSSignposter(subsystem: aeroSpaceAppId, category: .pointsOfInterest) let myPid = NSRunningApplication.current.processIdentifier let lockScreenAppBundleId = "com.apple.loginwindow" func interceptTermination(_ _signal: Int32) { signal(_signal, { signal in check(Thread.current.isMainThread) Task { defer { exit(signal) } try await terminationHandler.beforeTermination() } } as sig_t) } @MainActor func initTerminationHandler() { terminationHandler = AppServerTerminationHandler() } private struct AppServerTerminationHandler: TerminationHandler { func beforeTermination() async throws { try await makeAllWindowsVisibleAndRestoreSize() await toggleReleaseServerIfDebug(.on) } } @MainActor private func makeAllWindowsVisibleAndRestoreSize() async throws { // Make all windows fullscreen before Quit for (_, window) in MacWindow.allWindowsMap { // makeAllWindowsVisibleAndRestoreSize may be invoked when something went wrong (e.g. some windows are unbound) // that's why it's not allowed to use `.parent` call in here let monitor = try await window.getCenter()?.monitorApproximation ?? mainMonitor let monitorVisibleRect = monitor.visibleRect let windowSize = window.lastFloatingSize ?? CGSize(width: monitorVisibleRect.width, height: monitorVisibleRect.height) let point = CGPoint( x: (monitorVisibleRect.width - windowSize.width) / 2, y: (monitorVisibleRect.height - windowSize.height) / 2, ) try await window.setAxFrameBlocking(point, windowSize) } } @MainActor func terminateApp() -> Never { NSApplication.shared.terminate(nil) die("Unreachable code") } extension String { func copyToClipboard() { let pasteboard = NSPasteboard.general pasteboard.declareTypes([.string], owner: nil) pasteboard.setString(self, forType: .string) } } func - (a: CGPoint, b: CGPoint) -> CGPoint { CGPoint(x: a.x - b.x, y: a.y - b.y) } func + (a: CGPoint, b: CGPoint) -> CGPoint { CGPoint(x: a.x + b.x, y: a.y + b.y) } extension CGPoint: ConvenienceCopyable {} extension CGPoint { func distance(toOuterFrame rect: Rect) -> CGFloat { if rect.contains(self) { return 0 } let list: [CGFloat] = (rect.minY.until(excl: rect.maxY)?.contains(y) == true ? [abs(rect.minX - x), abs(rect.maxX - x)] : []) + (rect.minX.until(excl: rect.maxX)?.contains(x) == true ? [abs(rect.minY - y), abs(rect.maxY - y)] : []) + [ distance(to: rect.topLeftCorner), distance(to: rect.bottomRightCorner), distance(to: rect.topRightCorner), distance(to: rect.bottomLeftCorner), ] return list.minOrDie() } func coerce(in rect: Rect) -> CGPoint? { guard let xRange = rect.minX.until(incl: rect.maxX - 1) else { return nil } guard let yRange = rect.minY.until(incl: rect.maxY - 1) else { return nil } return CGPoint(x: x.coerce(in: xRange), y: y.coerce(in: yRange)) } func addingXOffset(_ offset: CGFloat) -> CGPoint { CGPoint(x: x + offset, y: y) } func addingYOffset(_ offset: CGFloat) -> CGPoint { CGPoint(x: x, y: y + offset) } func addingOffset(_ orientation: Orientation, _ offset: CGFloat) -> CGPoint { orientation == .h ? addingXOffset(offset) : addingYOffset(offset) } func getProjection(_ orientation: Orientation) -> Double { orientation == .h ? x : y } var vectorLength: CGFloat { sqrt(x * x + y * y) } func distance(to point: CGPoint) -> Double { (self - point).vectorLength } var monitorApproximation: Monitor { monitors.minByOrDie { distance(toOuterFrame: $0.rect) } } } extension CGFloat { func div(_ denominator: Int) -> CGFloat? { denominator == 0 ? nil : self / CGFloat(denominator) } func coerce(in range: ClosedRange) -> CGFloat { switch true { case self > range.upperBound: range.upperBound case self < range.lowerBound: range.lowerBound default: self } } } extension CGPoint: @retroactive Hashable { // todo migrate to self written Point public func hash(into hasher: inout Hasher) { hasher.combine(x) hasher.combine(y) } } #if DEBUG let isDebug = true #else let isDebug = false #endif @inlinable func checkCancellation() throws(CancellationError) { if Task.isCancelled { throw CancellationError() } } ================================================ FILE: Sources/AppBundle/util/axTrustedCheckOptionPrompt.swift ================================================ @preconcurrency import ApplicationServices let axTrustedCheckOptionPrompt: String = kAXTrustedCheckOptionPrompt.takeRetainedValue() as String ================================================ FILE: Sources/AppBundle/util/dumpAxRecursive.swift ================================================ import AppKit import Common func dumpAxRecursive(_ ax: AXUIElement, _ kind: AxKind, recursionDepth: Int = 0) -> [String: Json] { if recursionDepth > 5 { return [ "dumpAxRecursive infinite recursion": .bool(true), kAXAeroSynthetic: .bool(true), ] } let recursionDepth = recursionDepth + 1 var result: [String: Json] = [:] var ignored: [String] = [] var writable: [String] = [] var failedAxRequest: [String] = [] for key: String in ax.attrs(failedAxRequest: &failedAxRequest) { if globalIgnore.contains(key) || kindSpecificIgnore[kind]?.contains(key) == true { ignored.append(key) } else { var raw: AnyObject? if let status = .some(AXUIElementCopyAttributeValue(ax, key as CFString, &raw)), status != .success { failedAxRequest.append("get.\(key)(\(status.repr))") } result[key] = prettyValue(raw as Any?, recursionDepth: recursionDepth) var isWritable: DarwinBoolean = false if let status = .some(AXUIElementIsAttributeSettable(ax, key as CFString, &isWritable)), status != .success { failedAxRequest.append("isWritable.\(key)(\(status.repr))") } if isWritable.boolValue { writable.append(key) } } } if !writable.isEmpty { result["Aero.AxWritable"] = .string(writable.joined(separator: ", ")) } if !failedAxRequest.isEmpty { result["Aero.AxFailed"] = .string(failedAxRequest.joined(separator: ", ")) } if !ignored.isEmpty { result["Aero.AxIgnored"] = .string(ignored.joined(separator: ", ")) } return result } enum AxKind: Hashable { case button case window case app } private func prettyValue(_ value: Any?, recursionDepth: Int) -> Json { if let arr = value as? [Any?] { return .array(arr.map { prettyValue($0, recursionDepth: recursionDepth) }) } if let value = value as? Int { return .int(value) } if let value = value as? UInt32 { return .uint32(value) } if let value = value as? Bool { return .bool(value) } if let value { let ax = value as! AXUIElement if ax.get(Ax.roleAttr) == kAXButtonRole { return .dict(dumpAxRecursive(ax, .button, recursionDepth: recursionDepth)) } if let windowId = ax.containingWindowId() { let title = ax.get(Ax.titleAttr)?.doubleQuoted ?? "nil" let role = ax.get(Ax.roleAttr)?.doubleQuoted ?? "nil" let subrole = ax.get(Ax.subroleAttr)?.doubleQuoted ?? "nil" return .string("AXUIElement(AxWindowId=\(windowId), title=\(title), role=\(role), subrole=\(subrole))") } return .string(String(describing: value)) } return .null } extension AXUIElement { fileprivate func attrs(failedAxRequest: inout [String]) -> [String] { var rawArray: CFArray? if let status = .some(AXUIElementCopyAttributeNames(self, &rawArray)), status != .success { failedAxRequest.append("AXUIElementCopyAttributeNames(\(status.repr))") } return rawArray as? [String] ?? [] } } private let globalIgnore: Set = [ "AXChildren", // too verbose "AXChildrenInNavigationOrder", // too verbose "AXFocusableAncestor", // infinite recursion kAXHelpAttribute, // localized - not helpful kAXRoleDescriptionAttribute, // localized - not helpful ] private let kindSpecificIgnore: [AxKind: Set] = [ .button: [ "AXFrame", kAXEditedAttribute, kAXFocusedAttribute, kAXPositionAttribute, kAXSizeAttribute, ], .app: [ "AXEnhancedUserInterface", "AXPreferredLanguage", kAXHiddenAttribute, ], ] extension AXError { fileprivate var repr: String { switch self { case .actionUnsupported: "actionUnsupported" case .apiDisabled: "apiDisabled" case .attributeUnsupported: "attributeUnsupported" case .cannotComplete: "cannotComplete" case .failure: "failure" case .illegalArgument: "illegalArgument" case .invalidUIElement: "invalidUIElement" case .invalidUIElementObserver: "invalidUIElementObserver" case .noValue: "noValue" case .notEnoughPrecision: "notEnoughPrecision" case .notImplemented: "notImplemented" case .notificationAlreadyRegistered: "notificationAlreadyRegistered" case .notificationNotRegistered: "notificationNotRegistered" case .notificationUnsupported: "notificationUnsupported" case .parameterizedAttributeUnsupported: "parameterizedAttributeUnsupported" case .success: "success" @unknown default: rawValue.description } } } ================================================ FILE: Sources/AppBundle/windowLevelCache.swift ================================================ import CoreGraphics import Foundation @MainActor private var cache: [UInt32: MacOsWindowLevel] = [:] @MainActor func getWindowLevel(for windowId: UInt32) -> MacOsWindowLevel? { if let existing = cache[windowId] { return existing } var result: [UInt32: MacOsWindowLevel] = [:] let options = CGWindowListOption(arrayLiteral: .excludeDesktopElements, .optionOnScreenOnly) guard let cfArray = CGWindowListCopyWindowInfo(options, CGWindowID(0)) as? [CFDictionary] else { return nil } for elem in cfArray { let dict = elem as NSDictionary guard let _windowLayer = dict[kCGWindowLayer] else { continue } let windowLayer = ((_windowLayer as! CFNumber) as NSNumber).intValue guard let _windowId = dict[kCGWindowNumber] else { continue } let windowId = ((_windowId as! CFNumber) as NSNumber).uint32Value result[windowId] = .new(windowLevel: windowLayer) } cache = result return result[windowId] } enum MacOsWindowLevel: Sendable, Equatable { case normalWindow case alwaysOnTopWindow case unknown(windowLevel: Int) static func new(windowLevel: Int) -> MacOsWindowLevel { switch windowLevel { case 0: .normalWindow case 3: .alwaysOnTopWindow default: .unknown(windowLevel: windowLevel) } } static func fromJson(_ json: Json) -> MacOsWindowLevel? { switch json { case .string(let str) where str == "normalWindow": .normalWindow case .string(let str) where str == "alwaysOnTopWindow": .alwaysOnTopWindow case .int(let int): .new(windowLevel: int) default: nil } } func toJson() -> Json { switch self { case .normalWindow: .string("normalWindow") case .alwaysOnTopWindow: .string("alwaysOnTopWindow") case .unknown(let layerNumber): .int(layerNumber) } } } ================================================ FILE: Sources/AppBundleTests/AxUiElementWindowTypeTest.swift ================================================ @testable import AppBundle import Common import XCTest final class AxWindowKindTest: XCTestCase { func test() throws { try checkAxDumpsRecursive(projectRoot.appending(path: "./axDumps")) } } func checkAxDumpsRecursive(_ dir: URL) throws { for file in try FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) { if file.isDirectory { try checkAxDumpsRecursive(file) continue } if file.pathExtension == "md" { continue } let rawJson = try JSONSerialization.jsonObject(with: Data.init(contentsOf: file), options: [.json5Allowed]) as! [String: Any] let json = Json.newOrDie(rawJson).asDictOrDie let app = json["Aero.AXApp"]!.asDictOrDie let appBundleId = (rawJson["Aero.App.appBundleId"] as? String).flatMap { KnownBundleId.init(rawValue: $0) } let windowLevel = json["Aero.windowLevel"].map { MacOsWindowLevel.fromJson($0) ?? dieT() } let activationPolicy: NSApplication.ActivationPolicy = .from(string: rawJson["Aero.App.nsApp.activationPolicy"] as! String) assertEquals( json.getWindowType(axApp: app, appBundleId, activationPolicy, windowLevel), AxUiElementWindowType(rawValue: rawJson["Aero.AxUiElementWindowType"] as? String ?? dieT()), additionalMsg: "\(file.path()):0:0: AxUiElementWindowType doesn't match", ) assertEquals( json.isDialogHeuristic(appBundleId, windowLevel), rawJson["Aero.AxUiElementWindowType_isDialogHeuristic"] as? Bool ?? dieT(), additionalMsg: "\(file.path()):0:0: AxUiElementWindowType_isDialogHeuristic doesn't match", ) } } extension [String: Json]: AxUiElementMock { public func get(_ attr: Attr) -> Attr.T? where Attr: ReadableAttr { guard let value = self[attr.key] else { return isSynthetic ? dieT("\(self) doesn't contain \(attr.key)") : nil } if let value = value.rawValue { return attr.getter(value as AnyObject) ?? dieT("Value \(value) (of type \(Swift.type(of: value))) isn't convertible to \(attr.key)") } else { return nil } } private var isSynthetic: Bool { self[kAXAeroSynthetic] != nil } public func containingWindowId() -> CGWindowID? { _containingWindowId() } private func _containingWindowId() -> CGWindowID { let windowId = self["Aero.axWindowId"]?.rawValue ?? dieT() if let windowId = windowId as? Int { return UInt32.init(windowId) } else { return windowId as? UInt32 ?? dieT() } } } extension NSApplication.ActivationPolicy { static func from(string: String) -> NSApplication.ActivationPolicy { switch string { case "regular": .regular case "accessory": .accessory case "prohibited": .prohibited default: dieT("Unknown ActivationPolicy \(string)") } } } extension URL { var isDirectory: Bool { (try? resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory == true } } ================================================ FILE: Sources/AppBundleTests/assert.swift ================================================ // periphery:ignore:all - Those are utils that can become useful any moment @testable import AppBundle import Common import XCTest func assertTrue(_ actual: Bool, file: String = #filePath, line: Int = #line) { assertEquals(actual, true, file: file, line: line) } // Because assertEquals default messages are unreadable! // periphery:ignore func assertNotEquals(_ actual: T, _ expected: T, file: String = #filePath, line: Int = #line) where T: Equatable { if actual == expected { failExpectedActual("not \(expected)", actual, file: file, line: line) } } func assertNil(_ actual: Any?, file: String = #filePath, line: Int = #line) { if let actual { failExpectedActual("nil", actual, file: file, line: line) } } func assertNotNil(_ actual: Any?, file: String = #filePath, line: Int = #line) { if actual == nil { failExpectedActual("not nil", "nil", file: file, line: line) } } func assertEquals(_ actual: T, _ expected: T, additionalMsg: String? = nil, file: String = #filePath, line: Int = #line) where T: Equatable { if actual != expected { failExpectedActual(expected, actual, additionalMsg: additionalMsg, file: file, line: line) } } func assertSucc(_ actual: Result, file: String = #filePath, line: Int = #line) { switch actual { case .failure: failExpectedActual("Result.success", actual, file: file, line: line) case .success: break } } func assertSucc(_ actual: Result, _ expected: T, file: String = #filePath, line: Int = #line) where T: Equatable { switch actual { case .failure: failExpectedActual("Result.success", actual, file: file, line: line) case .success(let actual): assertEquals(actual, expected, file: file, line: line) } } func assertFail(_ actual: Result, _ expected: F? = nil, file: String = #filePath, line: Int = #line) where F: Equatable { switch actual { case .success: failExpectedActual("Result.failure", actual, file: file, line: line) case .failure(let actual): if let expected { assertEquals(actual, expected, file: file, line: line) } } } func testParseCommandSucc(_ command: String, _ expected: any CmdArgs) { let parsed = parseCommand(command) switch parsed { case .cmd(let command): if !command.args.equals(expected) { failExpectedActual(expected, command.args) } case .help: die() // todo test help case .failure(let msg): XCTFail(msg) } } func failExpectedActual(_ expected: Any?, _ actual: Any?, additionalMsg: String? = nil, file: String = #filePath, line: Int = #line) { let additionalMsg = additionalMsg.map { "\n Additional Message:\n \($0)" } ?? "" XCTFail( """ \(file):\(line): Assertion failed\(additionalMsg) Expected: \(expected.prettyDescription) Actual: \(actual.prettyDescription) """, ) } ================================================ FILE: Sources/AppBundleTests/command/BalanceSizesCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class BalanceSizesCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testBalanceSizesCommand() async throws { let workspace = Workspace.get(byName: name).apply { wsp in wsp.rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0).setWeight(wsp.rootTilingContainer.orientation, 1) TestWindow.new(id: 2, parent: $0).setWeight(wsp.rootTilingContainer.orientation, 2) TestWindow.new(id: 3, parent: $0).setWeight(wsp.rootTilingContainer.orientation, 3) } } try await BalanceSizesCommand(args: BalanceSizesCmdArgs(rawArgs: [])) .run(.defaultEnv.copy(\.workspaceName, name), .emptyStdin) for window in workspace.rootTilingContainer.children { assertEquals(window.getWeight(workspace.rootTilingContainer.orientation), 1) } } } ================================================ FILE: Sources/AppBundleTests/command/CloseCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class CloseCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testSimple() async throws { Workspace.get(byName: name).rootTilingContainer.apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) assertEquals(focus.workspace.rootTilingContainer.children.count, 2) try await CloseCommand(args: CloseCmdArgs(rawArgs: [])).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) assertEquals(focus.workspace.rootTilingContainer.children.count, 1) } func testCloseViaWindowIdFlag() async throws { Workspace.get(byName: name).rootTilingContainer.apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) assertEquals(focus.workspace.rootTilingContainer.children.count, 2) try await CloseCommand(args: CloseCmdArgs(rawArgs: []).copy(\.windowId, 2)).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) assertEquals(focus.workspace.rootTilingContainer.children.count, 1) } } ================================================ FILE: Sources/AppBundleTests/command/ExecCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class ExecCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParseExecCommand() { testParseCommandSucc("exec-and-forget echo 'foo'", ExecAndForgetCmdArgs(bashScript: " echo 'foo'")) } } ================================================ FILE: Sources/AppBundleTests/command/FlattenWorkspaceTreeCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class FlattenWorkspaceTreeCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testSimple() async throws { let workspace = Workspace.get(byName: name).apply { $0.rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 2, parent: $0) } } TestWindow.new(id: 3, parent: $0) // floating } assertEquals(workspace.focusWorkspace(), true) try await FlattenWorkspaceTreeCommand(args: FlattenWorkspaceTreeCmdArgs(rawArgs: [])).run(.defaultEnv, .emptyStdin) workspace.normalizeContainers() assertEquals(workspace.layoutDescription, .workspace([.h_tiles([.window(1), .window(2)]), .window(3)])) } } ================================================ FILE: Sources/AppBundleTests/command/FocusCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest // todo write tests // // test 1 // horizontal // window1 // vertical // vertical // window2 <-- focused // vertical // window5 // horizontal // window3 // window4 // pre-condition: focus_wrapping force_workspace // action: focus up // expected: mru(window3, window4) is focused @MainActor final class FocusCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParse() { XCTAssertTrue(parseCommand("focus --boundaries left").errorOrNil?.contains("Possible values") == true) var expected = FocusCmdArgs(rawArgs: [], cardinalOrDfsDirection: .direction(.left)) expected.rawBoundaries = .workspace testParseCommandSucc("focus --boundaries workspace left", expected) assertEquals( parseCommand("focus --boundaries workspace --boundaries workspace left").errorOrNil, "ERROR: Duplicated option '--boundaries'", ) assertEquals( parseCommand("focus --window-id 42 --ignore-floating").errorOrNil, "--window-id is incompatible with other options", ) assertEquals( parseCommand("focus --boundaries all-monitors-outer-frame dfs-next").errorOrNil, "(dfs-next|dfs-prev) only supports --boundaries workspace", ) assertEquals( parseCommand("focus --window-id 42 --wrap-around").errorOrNil, "--window-id is incompatible with other options", ) assertEquals( parseCommand("focus left --boundaries-action wrap-around-the-workspace --wrap-around").errorOrNil, "ERROR: Conflicting options: --boundaries-action, --wrap-around", ) } func testFocus() { assertEquals(focus.windowOrNil, nil) Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 2) } func testFocusOverFloatingWindows() async throws { assertEquals(focus.windowOrNil, nil) Workspace.get(byName: name).apply { TestWindow.new(id: 1, parent: $0, rect: Rect(topLeftX: 0, topLeftY: 0, width: 100, height: 100)) assertEquals(TestWindow.new(id: 2, parent: $0, rect: Rect(topLeftX: 10, topLeftY: 10, width: 100, height: 100)).focusWindow(), true) TestWindow.new(id: 3, parent: $0, rect: Rect(topLeftX: 20, topLeftY: 20, width: 100, height: 100)) } assertEquals(focus.windowOrNil?.windowId, 2) try await FocusCommand.new(direction: .right).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 3) } func testFocusAlongTheContainerOrientation() async throws { Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) try await FocusCommand.new(direction: .right).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) } func testFocusAcrossTheContainerOrientation() async throws { Workspace.get(byName: name).apply { TestWindow.new(id: 1, parent: $0.rootTilingContainer) TestWindow.new(id: 2, parent: $0.rootTilingContainer) assertEquals($0.focusWorkspace(), true) } assertEquals(focus.windowOrNil?.windowId, 2) try await FocusCommand.new(direction: .up).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) try await FocusCommand.new(direction: .down).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) } func testFocusNoWrapping() async throws { Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) try await FocusCommand.new(direction: .left).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) } func testFocusWrapping() async throws { Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) var args = FocusCmdArgs(rawArgs: [], cardinalOrDfsDirection: .direction(.left)) args.rawBoundaries = .workspace args.rawBoundariesAction = .wrapAroundTheWorkspace try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) } func testFocusFindMruLeaf() async throws { let workspace = Workspace.get(byName: name) var startWindow: Window! var window2: Window! var window3: Window! var unrelatedWindow: Window! workspace.rootTilingContainer.apply { startWindow = TestWindow.new(id: 1, parent: $0) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { window2 = TestWindow.new(id: 2, parent: $0) unrelatedWindow = TestWindow.new(id: 5, parent: $0) } window3 = TestWindow.new(id: 3, parent: $0) } } assertEquals(workspace.mostRecentWindowRecursive?.windowId, 3) // The latest bound _ = startWindow.focusWindow() try await FocusCommand.new(direction: .right).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 3) window2.markAsMostRecentChild() _ = startWindow.focusWindow() try await FocusCommand.new(direction: .right).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) window3.markAsMostRecentChild() unrelatedWindow.markAsMostRecentChild() _ = startWindow.focusWindow() try await FocusCommand.new(direction: .right).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) } func testFocusOutsideOfTheContainer() async throws { Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) } } try await FocusCommand.new(direction: .left).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) } func testFocusOutsideOfTheContainer2() async throws { Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) } } try await FocusCommand.new(direction: .left).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) } func testFocusDfsRelative() async throws { Workspace.get(byName: name).rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } } TestWindow.new(id: 4, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) try await FocusCommand.new(dfsRelative: .dfsNext).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) try await FocusCommand.new(dfsRelative: .dfsNext).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 3) try await FocusCommand.new(dfsRelative: .dfsNext).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 4) try await FocusCommand.new(dfsRelative: .dfsPrev).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 3) try await FocusCommand.new(dfsRelative: .dfsPrev).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 2) try await FocusCommand.new(dfsRelative: .dfsPrev).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) } func testFocusDfsRelativeWrapping() async throws { Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } assertEquals(focus.windowOrNil?.windowId, 1) var args = FocusCmdArgs(rawArgs: [], cardinalOrDfsDirection: .dfsRelative(.dfsPrev)) args.rawBoundariesAction = .stop assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 0) assertEquals(focus.windowOrNil?.windowId, 1) args.rawBoundariesAction = .fail assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 1) assertEquals(focus.windowOrNil?.windowId, 1) args.rawBoundariesAction = .wrapAroundTheWorkspace assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 0) assertEquals(focus.windowOrNil?.windowId, 2) args.cardinalOrDfsDirection = .dfsRelative(.dfsNext) args.rawBoundariesAction = .stop assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 0) assertEquals(focus.windowOrNil?.windowId, 2) args.rawBoundariesAction = .fail assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 1) assertEquals(focus.windowOrNil?.windowId, 2) args.rawBoundariesAction = .wrapAroundTheWorkspace assertEquals(try await FocusCommand(args: args).run(.defaultEnv, .emptyStdin).exitCode, 0) assertEquals(focus.windowOrNil?.windowId, 1) } } extension FocusCommand { static func new(direction: CardinalDirection) -> FocusCommand { FocusCommand(args: FocusCmdArgs(rawArgs: [], cardinalOrDfsDirection: .direction(direction))) } static func new(dfsRelative: DfsNextPrev) -> FocusCommand { FocusCommand(args: FocusCmdArgs(rawArgs: [], cardinalOrDfsDirection: .dfsRelative(dfsRelative))) } } ================================================ FILE: Sources/AppBundleTests/command/JoinWithCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class JoinWithCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testMoveIn() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 0, parent: $0) assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } try await JoinWithCommand(args: JoinWithCmdArgs(rawArgs: [], direction: .right)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([ .window(0), .v_tiles([ .window(1), .window(2), ]), ])) } } ================================================ FILE: Sources/AppBundleTests/command/ListAppsTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ListAppsTest: XCTestCase { func testParse() { assertNotNil(parseCommand("list-apps --macos-native-hidden").cmdOrNil) assertNotNil(parseCommand("list-apps --macos-native-hidden no").cmdOrNil) assertNotNil(parseCommand("list-apps --format %{app-bundle-id}").cmdOrNil) assertNotNil(parseCommand("list-apps --count").cmdOrNil) assertEquals(parseCommand("list-apps --format %{app-bundle-id} --count").errorOrNil, "ERROR: Conflicting options: --count, --format") } } ================================================ FILE: Sources/AppBundleTests/command/ListModesTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ListModesTest: XCTestCase { func testParseListModesCommand() { testParseCommandSucc("list-modes", ListModesCmdArgs(rawArgs: [])) testParseCommandSucc("list-modes --current", ListModesCmdArgs(rawArgs: []).copy(\.current, true)) testParseCommandSucc("list-modes --json", ListModesCmdArgs(rawArgs: []).copy(\.json, true)) testParseCommandSucc("list-modes --count", ListModesCmdArgs(rawArgs: []).copy(\.outputOnlyCount, true)) testParseCommandSucc("list-modes --current --json", ListModesCmdArgs(rawArgs: []).copy(\.current, true).copy(\.json, true)) } func testParseListModesCommandConflicts() { assertEquals(parseCommand("list-modes --json --count").errorOrNil, "ERROR: Conflicting options: --count, --json") assertEquals(parseCommand("list-modes --current --count").errorOrNil, "ERROR: Conflicting options: --count, --current") } @MainActor func testListModesOutput() async throws { config.modes = [ "main": Mode(bindings: [:]), "service": Mode(bindings: [:]), "resize": Mode(bindings: [:]), ] let defaultResult = try await ListModesCommand(args: ListModesCmdArgs(rawArgs: [])).run(.defaultEnv, .emptyStdin) assertEquals(defaultResult.exitCode, 0) assertEquals(defaultResult.stdout, ["main", "resize", "service"]) assertEquals(defaultResult.stderr, []) let currentResult = try await ListModesCommand(args: ListModesCmdArgs(rawArgs: []).copy(\.current, true)).run(.defaultEnv, .emptyStdin) assertEquals(currentResult.exitCode, 0) assertEquals(currentResult.stdout, ["main"]) assertEquals(currentResult.stderr, []) let countResult = try await ListModesCommand(args: ListModesCmdArgs(rawArgs: []).copy(\.outputOnlyCount, true)).run(.defaultEnv, .emptyStdin) assertEquals(countResult.exitCode, 0) assertEquals(countResult.stdout, ["3"]) assertEquals(countResult.stderr, []) let jsonResult = try await ListModesCommand(args: ListModesCmdArgs(rawArgs: []).copy(\.json, true)).run(.defaultEnv, .emptyStdin) let expectedJson = JSONEncoder.aeroSpaceDefault.encodeToString([ ["mode-id": "main"], ["mode-id": "resize"], ["mode-id": "service"], ]) assertEquals(jsonResult.exitCode, 0) assertEquals(jsonResult.stdout, [expectedJson]) assertEquals(jsonResult.stderr, []) let currentJsonResult = try await ListModesCommand(args: ListModesCmdArgs(rawArgs: []).copy(\.current, true).copy(\.json, true)).run(.defaultEnv, .emptyStdin) let expectedCurrentJson = JSONEncoder.aeroSpaceDefault.encodeToString([ ["mode-id": "main"], ]) assertEquals(currentJsonResult.exitCode, 0) assertEquals(currentJsonResult.stdout, [expectedCurrentJson]) assertEquals(currentJsonResult.stderr, []) } } ================================================ FILE: Sources/AppBundleTests/command/ListMonitorsTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ListMonitorsTest: XCTestCase { func testParseListMonitorsCommand() { testParseCommandSucc("list-monitors", ListMonitorsCmdArgs(rawArgs: [])) testParseCommandSucc("list-monitors --focused", ListMonitorsCmdArgs(rawArgs: []).copy(\.focused, true)) testParseCommandSucc("list-monitors --count", ListMonitorsCmdArgs(rawArgs: []).copy(\.outputOnlyCount, true)) assertEquals(parseCommand("list-monitors --format %{monitor-id} --count").errorOrNil, "ERROR: Conflicting options: --count, --format") } } ================================================ FILE: Sources/AppBundleTests/command/ListWindowsTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class ListWindowsTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParse() { assertEquals(parseCommand("list-windows --pid 1").errorOrNil, "Mandatory option is not specified (--focused|--all|--monitor|--workspace)") assertNil(parseCommand("list-windows --workspace M --pid 1").errorOrNil) assertEquals(parseCommand("list-windows --pid 1 --focused").errorOrNil, "--focused conflicts with other \"filtering\" flags") assertEquals(parseCommand("list-windows --pid 1 --all").errorOrNil, "--all conflicts with \"filtering\" flags. Please use '--monitor all' instead of '--all' alias") assertNil(parseCommand("list-windows --all").errorOrNil) assertEquals(parseCommand("list-windows --all --workspace M").errorOrNil, "ERROR: Conflicting options: --all, --workspace") assertEquals(parseCommand("list-windows --all --focused").errorOrNil, "ERROR: Conflicting options: --all, --focused") assertEquals(parseCommand("list-windows --all --count --format %{window-title}").errorOrNil, "ERROR: Conflicting options: --count, --format") assertEquals( parseCommand("list-windows --all --focused --monitor mouse").errorOrNil, "ERROR: Conflicting options: --all, --focused") assertEquals( parseCommand("list-windows --all --focused --monitor mouse --workspace focused").errorOrNil, "ERROR: Conflicting options: --all, --focused, --workspace") assertEquals( parseCommand("list-windows --all --workspace focused").errorOrNil, "ERROR: Conflicting options: --all, --workspace") assertNil(parseCommand("list-windows --monitor mouse").errorOrNil) // --json assertEquals(parseCommand("list-windows --all --count --json").errorOrNil, "ERROR: Conflicting options: --count, --json") assertEquals(parseCommand("list-windows --all --format '%{right-padding}' --json").errorOrNil, "%{right-padding} interpolation variable is not allowed when --json is used") assertEquals(parseCommand("list-windows --all --format '%{window-title} |' --json").errorOrNil, "Only interpolation variables and spaces are allowed in \'--format\' when \'--json\' is used") assertNil(parseCommand("list-windows --all --format '%{window-title}' --json").errorOrNil) } func testInterpolationVariablesConsistency() { for kind in AeroObjKind.allCases { switch kind { case .window: assertTrue(FormatVar.WindowFormatVar.allCases.allSatisfy { $0.rawValue.starts(with: "window-") }) case .app: assertTrue(FormatVar.AppFormatVar.allCases.allSatisfy { $0.rawValue.starts(with: "app-") }) case .workspace: assertTrue(FormatVar.WorkspaceFormatVar.allCases.allSatisfy { $0.rawValue.starts(with: "workspace") }) case .monitor: assertTrue(FormatVar.MonitorFormatVar.allCases.allSatisfy { $0.rawValue.starts(with: "monitor-") }) } } } func testFormat() { Workspace.get(byName: name).rootTilingContainer.apply { let windows = [ AeroObj.window(window: TestWindow.new(id: 2, parent: $0), title: "non-empty"), AeroObj.window(window: TestWindow.new(id: 1, parent: $0), title: ""), ] assertEquals(windows.format([.interVar("window-title")]), .success(["non-empty", ""])) } Workspace.get(byName: name).rootTilingContainer.apply { let windows = [ AeroObj.window(window: TestWindow.new(id: 2, parent: $0), title: "non-empty"), AeroObj.window(window: TestWindow.new(id: 10, parent: $0), title: ""), ] assertEquals(windows.format([.interVar("window-id"), .interVar("right-padding"), .interVar("window-title")]), .success(["2 non-empty", "10"])) } Workspace.get(byName: name).rootTilingContainer.apply { let windows = [ AeroObj.window(window: TestWindow.new(id: 2, parent: $0), title: "title1"), AeroObj.window(window: TestWindow.new(id: 10, parent: $0), title: "title2"), ] assertEquals(windows.format([.interVar("window-id"), .interVar("right-padding"), .literal(" | "), .interVar("window-title")]), .success(["2 | title1", "10 | title2"])) } } } ================================================ FILE: Sources/AppBundleTests/command/ListWorkspacesTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ListWorkspacesTest: XCTestCase { func testParse() { assertNotNil(parseCommand("list-workspaces --all").cmdOrNil) assertNil(parseCommand("list-workspaces --all --visible").cmdOrNil) assertNil(parseCommand("list-workspaces --focused --visible").cmdOrNil) assertNil(parseCommand("list-workspaces --focused --all").cmdOrNil) assertNil(parseCommand("list-workspaces --visible").cmdOrNil) assertNotNil(parseCommand("list-workspaces --visible --monitor 2").cmdOrNil) assertNotNil(parseCommand("list-workspaces --monitor focused").cmdOrNil) assertNil(parseCommand("list-workspaces --focused --monitor 2").cmdOrNil) assertNotNil(parseCommand("list-workspaces --all --format %{workspace}").cmdOrNil) assertEquals(parseCommand("list-workspaces --all --format %{workspace} --count").errorOrNil, "ERROR: Conflicting options: --count, --format") assertEquals(parseCommand("list-workspaces --empty").errorOrNil, "Mandatory option is not specified (--all|--focused|--monitor)") assertEquals(parseCommand("list-workspaces --all --focused --monitor mouse").errorOrNil, "ERROR: Conflicting options: --all, --focused, --monitor") } } ================================================ FILE: Sources/AppBundleTests/command/MoveCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class MoveCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testMove_swapWindows() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .right)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(2), .window(1)])) } func testMoveInto_findTopMostContainerWithRightOrientation() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 0, parent: $0) assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 2, parent: $0) } } } try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .right)).run(.defaultEnv, .emptyStdin) assertEquals( root.layoutDescription, .h_tiles([ .window(0), .h_tiles([ .window(1), .h_tiles([ .window(2), ]), ]), ]), ) } func testMove_mru() async throws { var window3: Window! let root = Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 0, parent: $0) assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 2, parent: $0) window3 = TestWindow.new(id: 3, parent: $0) } TestWindow.new(id: 4, parent: $0) } } window3.markAsMostRecentChild() try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .right)).run(.defaultEnv, .emptyStdin) assertEquals( root.layoutDescription, .h_tiles([ .window(0), .v_tiles([ .h_tiles([ .window(1), .window(2), .window(3), ]), .window(4), ]), ]), ) } func testSwap_preserveWeight() async throws { let root = Workspace.get(byName: name).rootTilingContainer let window1 = TestWindow.new(id: 1, parent: root, adaptiveWeight: 1) let window2 = TestWindow.new(id: 2, parent: root, adaptiveWeight: 2) _ = window2.focusWindow() try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .left)).run(.defaultEnv, .emptyStdin) assertEquals(window2.hWeight, 2) assertEquals(window1.hWeight, 1) } func testMoveIn_newWeight() async throws { var window1: Window! var window2: Window! Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 0, parent: $0, adaptiveWeight: 1) window1 = TestWindow.new(id: 1, parent: $0, adaptiveWeight: 2) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { window2 = TestWindow.new(id: 2, parent: $0, adaptiveWeight: 1) } } _ = window1.focusWindow() try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .right)).run(.defaultEnv, .emptyStdin) assertEquals(window2.hWeight, 1) assertEquals(window2.vWeight, 1) assertEquals(window1.vWeight, 1) assertEquals(window1.hWeight, 1) } func testCreateImplicitContainer() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) } let result = try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .up)).run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .v_tiles([ .window(2), .h_tiles([.window(1), .window(3)]), ]), ]), ) assertEquals(result.exitCode, 0) } func testStop_onRootNode() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } let result = try await parseCommand("move --boundaries-action stop left").cmdOrDie.run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .h_tiles([.window(1), .window(2), .window(3)]), ]), ) assertEquals(result.exitCode, 0) } func testStop_onRootNode_withOppositeOrientation() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } let result = try await parseCommand("move --boundaries-action stop up").cmdOrDie.run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .h_tiles([.window(1), .window(2), .window(3)]), ]), ) assertEquals(result.exitCode, 0) } func testStop_onRootNode_whenNoBoundary() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) } let result = try await parseCommand("move --boundaries-action stop left").cmdOrDie.run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .h_tiles([.window(2), .window(1), .window(3)]), ]), ) assertEquals(result.exitCode, 0) } func testStop_onInnerNode() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) } } let result = try await parseCommand("move --boundaries-action stop right").cmdOrDie.run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .h_tiles([.window(1), .v_tiles([.window(3)]), .window(2)]), ]), ) assertEquals(result.exitCode, 0) } func testFail() async throws { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } let result = try await parseCommand("move --boundaries-action fail left").cmdOrDie.run(.defaultEnv, .emptyStdin) assertEquals( workspace.layoutDescription, .workspace([ .h_tiles([.window(1), .window(2), .window(3)]), ]), ) assertEquals(result.exitCode, 1) } func testMoveOut() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) TestWindow.new(id: 4, parent: $0) } } try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .left)).run(.defaultEnv, .emptyStdin) assertEquals( root.layoutDescription, .h_tiles([ .window(1), .window(2), .v_tiles([ .window(3), .window(4), ]), ]), ) } func testMoveOutWithNormalization_right() async throws { config.enableNormalizationFlattenContainers = true let workspace = Workspace.get(byName: name).apply { TestWindow.new(id: 1, parent: $0.rootTilingContainer) assertEquals(TestWindow.new(id: 2, parent: $0.rootTilingContainer).focusWindow(), true) } try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .right)).run(.defaultEnv, .emptyStdin) assertEquals( workspace.rootTilingContainer.layoutDescription, .h_tiles([ .window(1), .window(2), ]), ) assertEquals(focus.windowOrNil?.windowId, 2) } func testMoveOutWithNormalization_left() async throws { config.enableNormalizationFlattenContainers = true let workspace = Workspace.get(byName: name).apply { assertEquals(TestWindow.new(id: 1, parent: $0.rootTilingContainer).focusWindow(), true) TestWindow.new(id: 2, parent: $0.rootTilingContainer) } try await MoveCommand(args: MoveCmdArgs(rawArgs: [], .left)).run(.defaultEnv, .emptyStdin) assertEquals( workspace.rootTilingContainer.layoutDescription, .h_tiles([ .window(1), .window(2), ]), ) assertEquals(focus.windowOrNil?.windowId, 1) } } extension TreeNode { var layoutDescription: LayoutDescription { return switch nodeCases { case .window(let window): .window(window.windowId) case .workspace(let workspace): .workspace(workspace.children.map(\.layoutDescription)) case .macosMinimizedWindowsContainer: .macosMinimized case .macosFullscreenWindowsContainer: .macosFullscreen case .macosHiddenAppsWindowsContainer: .macosHiddeAppWindow case .macosPopupWindowsContainer: .macosPopupWindowsContainer case .tilingContainer(let container): switch container.layout { case .tiles: container.orientation == .h ? .h_tiles(container.children.map(\.layoutDescription)) : .v_tiles(container.children.map(\.layoutDescription)) case .accordion: container.orientation == .h ? .h_accordion(container.children.map(\.layoutDescription)) : .v_accordion(container.children.map(\.layoutDescription)) } } } } enum LayoutDescription: Equatable { case workspace([LayoutDescription]) case h_tiles([LayoutDescription]) case v_tiles([LayoutDescription]) case h_accordion([LayoutDescription]) case v_accordion([LayoutDescription]) case window(UInt32) case macosPopupWindowsContainer case macosMinimized case macosHiddeAppWindow case macosFullscreen } ================================================ FILE: Sources/AppBundleTests/command/MoveNodeToMonitorCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class MoveNodeToMonitorCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParse() { testParseCommandSucc("move-node-to-monitor next", MoveNodeToMonitorCmdArgs(target: .relative(.next))) testParseCommandSucc("move-node-to-monitor --fail-if-noop main", MoveNodeToMonitorCmdArgs(target: .patterns([.main])).copy(\.failIfNoop, true)) assertEquals(parseCommand("move-node-to-monitor --fail-if-noop next").errorOrNil, "--fail-if-noop is incompatible with (left|down|up|right|next|prev)") assertEquals(parseCommand("move-node-to-monitor --fail-if-noop left").errorOrNil, "--fail-if-noop is incompatible with (left|down|up|right|next|prev)") } } ================================================ FILE: Sources/AppBundleTests/command/MoveNodeToWorkspaceCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class MoveNodeToWorkspaceCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParse() { testParseCommandSucc("move-node-to-workspace next", MoveNodeToWorkspaceCmdArgs(target: .relative(.next))) assertEquals(parseCommand("move-node-to-workspace --fail-if-noop next").errorOrNil, "--fail-if-noop is incompatible with (next|prev)") assertEquals(parseCommand("move-node-to-workspace --stdin foo").errorOrNil, "--stdin and --no-stdin require using (next|prev) argument") testParseCommandSucc("move-node-to-workspace --stdin next", MoveNodeToWorkspaceCmdArgs(target: .relative(.next)).copy(\.explicitStdinFlag, true)) testParseCommandSucc("move-node-to-workspace --no-stdin next", MoveNodeToWorkspaceCmdArgs(target: .relative(.next)).copy(\.explicitStdinFlag, false)) } func testSimple() async throws { let workspaceA = Workspace.get(byName: "a") workspaceA.rootTilingContainer.apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() } try await MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "b")).run(.defaultEnv, .emptyStdin) XCTAssertTrue(workspaceA.isEffectivelyEmpty) assertEquals((Workspace.get(byName: "b").rootTilingContainer.children.singleOrNil() as? Window)?.windowId, 1) } func testEmptyWorkspaceSubject() async throws { let workspaceA = Workspace.get(byName: "a") workspaceA.rootTilingContainer.apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() } try await MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "b")).run(.defaultEnv, .emptyStdin) assertEquals(focus.workspace.name, "a") } func testAnotherWindowSubject() async throws { Workspace.get(byName: "a").rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) _ = TestWindow.new(id: 2, parent: $0).focusWindow() } try await MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "b")).run(.defaultEnv, .emptyStdin) assertEquals(focus.windowOrNil?.windowId, 1) } func testPreserveFloatingLayout() async throws { let workspaceA = Workspace.get(byName: "a").apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() } try await MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "b")).run(.defaultEnv, .emptyStdin) XCTAssertTrue(workspaceA.isEffectivelyEmpty) assertEquals(Workspace.get(byName: "b").children.filterIsInstance(of: Window.self).singleOrNil()?.windowId, 1) } func testSummonWindow() async throws { let workspaceA = Workspace.get(byName: "a").apply { $0.rootTilingContainer.apply { _ = TestWindow.new(id: 1, parent: $0).focusWindow() } } Workspace.get(byName: "b").rootTilingContainer.apply { TestWindow.new(id: 2, parent: $0) } assertEquals(focus.workspace, workspaceA) try await MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "a").copy(\.windowId, 2)) .run(.defaultEnv, .emptyStdin) assertEquals(focus.workspace, workspaceA) assertEquals(focus.windowOrNil?.windowId, 1) assertEquals(Workspace.get(byName: "b").rootTilingContainer.children.count, 0) assertEquals(workspaceA.rootTilingContainer.children.count, 2) } } ================================================ FILE: Sources/AppBundleTests/command/ResizeCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ResizeCommandTest: XCTestCase { func testParseCommand() { testParseCommandSucc("resize smart +10", ResizeCmdArgs(rawArgs: [], dimension: .smart, units: .add(10))) testParseCommandSucc("resize smart -10", ResizeCmdArgs(rawArgs: [], dimension: .smart, units: .subtract(10))) testParseCommandSucc("resize smart 10", ResizeCmdArgs(rawArgs: [], dimension: .smart, units: .set(10))) testParseCommandSucc("resize smart-opposite +10", ResizeCmdArgs(rawArgs: [], dimension: .smartOpposite, units: .add(10))) testParseCommandSucc("resize smart-opposite -10", ResizeCmdArgs(rawArgs: [], dimension: .smartOpposite, units: .subtract(10))) testParseCommandSucc("resize smart-opposite 10", ResizeCmdArgs(rawArgs: [], dimension: .smartOpposite, units: .set(10))) testParseCommandSucc("resize height 10", ResizeCmdArgs(rawArgs: [], dimension: .height, units: .set(10))) testParseCommandSucc("resize width 10", ResizeCmdArgs(rawArgs: [], dimension: .width, units: .set(10))) testParseCommandFail("resize s 10", msg: """ ERROR: Can't parse 's'. Possible values: (width|height|smart|smart-opposite) """) testParseCommandFail("resize smart foo", msg: "ERROR: argument must be a number") } } ================================================ FILE: Sources/AppBundleTests/command/SplitCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class SplitCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testSplit() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } try await SplitCommand(args: SplitCmdArgs(rawArgs: [], .vertical)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([ .v_tiles([ .window(1), ]), .window(2), ])) } func testSplitOppositeOrientation() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } try await SplitCommand(args: SplitCmdArgs(rawArgs: [], .opposite)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([ .v_tiles([ .window(1), ]), .window(2), ])) } func testChangeOrientation() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) } TestWindow.new(id: 2, parent: $0) } try await SplitCommand(args: SplitCmdArgs(rawArgs: [], .horizontal)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([ .h_tiles([ .window(1), ]), .window(2), ])) } func testToggleOrientation() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) } TestWindow.new(id: 2, parent: $0) } try await SplitCommand(args: SplitCmdArgs(rawArgs: [], .opposite)).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([ .h_tiles([ .window(1), ]), .window(2), ])) } } ================================================ FILE: Sources/AppBundleTests/command/SubscribeCmdArgsTest.swift ================================================ @testable import AppBundle import Common import XCTest final class SubscribeCmdArgsTest: XCTestCase { func testParseValidEvents() { let result = parseSubscribeCmdArgs(["focus-changed", "mode-changed"].slice) switch result { case .cmd(let args): assertEquals(args.events, Set([.focusChanged, .modeChanged])) case .help, .failure: XCTFail("Expected success") } } func testParseAllFlag() { let result = parseSubscribeCmdArgs(["--all"].slice) switch result { case .cmd(let args): assertEquals(args.events, Set(ServerEventType.allCases)) case .help, .failure: XCTFail("Expected success") } } func testParseUnknownEvent() { let result = parseSubscribeCmdArgs(["unknown-event"].slice) switch result { case .cmd, .help: XCTFail("Expected failure") case .failure(let err): assertEquals(err, """ ERROR: Can't parse 'unknown-event'. Possible values: (focus-changed|focused-monitor-changed|focused-workspace-changed|mode-changed|window-detected|binding-triggered) """) } } func testParseDuplicateEvent() { let result = parseSubscribeCmdArgs(["focus-changed", "focus-changed"].slice) switch result { case .cmd, .help: XCTFail("Expected failure") case .failure(let err): assertEquals(err, "ERROR: Duplicate event 'focus-changed'") } } func testParseNoEvents() { let result = parseSubscribeCmdArgs([String]().slice) switch result { case .cmd, .help: XCTFail("Expected failure") case .failure(let err): assertEquals(err, "Either --all or at least one must be specified") } } func testParseAllWithEventsConflict() { let result = parseSubscribeCmdArgs(["--all", "focus-changed"].slice) switch result { case .cmd, .help: XCTFail("Expected failure") case .failure(let err): assertEquals(err, "--all conflicts with specifying individual events") } } } ================================================ FILE: Sources/AppBundleTests/command/SummonWorkspaceCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class SummonWorkspaceCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParse() { assertEquals(parseCommand("summon-workspace").errorOrNil, "ERROR: Argument '' is mandatory") } } ================================================ FILE: Sources/AppBundleTests/command/SwapCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class SwapCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testSwap_swapWindows_Directional() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } TestWindow.new(id: 3, parent: $0) } try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .direction(.right))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(3), .window(2)]), .window(1)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .direction(.left))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(1), .window(2)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .direction(.down))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(2), .window(1)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .direction(.up))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(1), .window(2)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) } func testSwap_swapWindows_DfsRelative() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) } TestWindow.new(id: 3, parent: $0) } try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .dfsRelative(.dfsNext))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(2), .window(1)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .dfsRelative(.dfsNext))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(2), .window(3)]), .window(1)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .dfsRelative(.dfsPrev))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(2), .window(1)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) try await SwapCommand(args: SwapCmdArgs(rawArgs: [], target: .dfsRelative(.dfsPrev))).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.v_tiles([.window(1), .window(2)]), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) } func testSwap_DirectionalWrapping() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } var args = SwapCmdArgs(rawArgs: [], target: .direction(.left)) args.wrapAround = true try await SwapCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(3), .window(2), .window(1)])) assertEquals(focus.windowOrNil?.windowId, 1) args.target = .initialized(.direction(.right)) try await SwapCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(1), .window(2), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) } func testSwap_DfsRelativeWrapping() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { assertEquals(TestWindow.new(id: 1, parent: $0).focusWindow(), true) TestWindow.new(id: 2, parent: $0) TestWindow.new(id: 3, parent: $0) } var args = SwapCmdArgs(rawArgs: [], target: .dfsRelative(.dfsPrev)) args.wrapAround = true try await SwapCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(3), .window(2), .window(1)])) assertEquals(focus.windowOrNil?.windowId, 1) args.target = .initialized(.dfsRelative(.dfsNext)) try await SwapCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(1), .window(2), .window(3)])) assertEquals(focus.windowOrNil?.windowId, 1) } func testSwap_SwapFocus() async throws { let root = Workspace.get(byName: name).rootTilingContainer.apply { TestWindow.new(id: 1, parent: $0) assertEquals(TestWindow.new(id: 2, parent: $0).focusWindow(), true) TestWindow.new(id: 3, parent: $0) } var args = SwapCmdArgs(rawArgs: [], target: .direction(.right)) args.swapFocus = true try await SwapCommand(args: args).run(.defaultEnv, .emptyStdin) assertEquals(root.layoutDescription, .h_tiles([.window(1), .window(3), .window(2)])) assertEquals(focus.windowOrNil?.windowId, 3) } } ================================================ FILE: Sources/AppBundleTests/command/WorkspaceCommandTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class WorkspaceCommandTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testParseWorkspaceCommand() { testParseCommandFail("workspace my mail", msg: "ERROR: Unknown argument 'mail'") testParseCommandFail("workspace 'my mail'", msg: "ERROR: Whitespace characters are forbidden in workspace names") assertEquals(parseCommand("workspace").errorOrNil, "ERROR: Argument '(|next|prev)' is mandatory") testParseCommandSucc("workspace next", WorkspaceCmdArgs(target: .relative(.next))) testParseCommandSucc("workspace --auto-back-and-forth W", WorkspaceCmdArgs(target: .direct(.parse("W").getOrDie()), autoBackAndForth: true)) assertEquals(parseCommand("workspace --wrap-around W").errorOrNil, "--wrapAround requires using (next|prev) argument") assertEquals(parseCommand("workspace --auto-back-and-forth next").errorOrNil, "--auto-back-and-forth is incompatible with (next|prev)") testParseCommandSucc("workspace next --wrap-around", WorkspaceCmdArgs(target: .relative(.next), wrapAround: true)) assertEquals(parseCommand("workspace --stdin foo").errorOrNil, "--stdin and --no-stdin require using (next|prev) argument") testParseCommandSucc("workspace --stdin next", WorkspaceCmdArgs(target: .relative(.next)).copy(\.explicitStdinFlag, true)) testParseCommandSucc("workspace --no-stdin next", WorkspaceCmdArgs(target: .relative(.next)).copy(\.explicitStdinFlag, false)) } } ================================================ FILE: Sources/AppBundleTests/config/ConfigTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class ConfigTest: XCTestCase { func testParseI3Config() { let toml = try! String(contentsOf: projectRoot.appending(component: "docs/config-examples/i3-like-config-example.toml"), encoding: .utf8) let (i3Config, errors) = parseConfig(toml) assertEquals(errors, []) assertEquals(i3Config.execConfig, defaultConfig.execConfig) assertEquals(i3Config.enableNormalizationFlattenContainers, false) assertEquals(i3Config.enableNormalizationOppositeOrientationForNestedContainers, false) } func testParseDefaultConfig() { let toml = try! String(contentsOf: projectRoot.appending(component: "docs/config-examples/default-config.toml"), encoding: .utf8) let (_, errors) = parseConfig(toml) assertEquals(errors, []) } func testConfigVersionOutOfBounds() { let (_, errors) = parseConfig( """ config-version = 0 """, ) assertEquals(errors.descriptions, ["config-version: Must be in [1, 2] range"]) } func testExecOnWorkspaceChangeDifferentTypesError() { let (_, errors) = parseConfig( """ exec-on-workspace-change = ['', 1] """, ) assertEquals(errors.descriptions, ["exec-on-workspace-change[1]: Expected type is \'string\'. But actual type is \'integer\'"]) } func testDuplicatedPersistentWorkspaces() { let (_, errors) = parseConfig( """ config-version = 2 persistent-workspaces = ['a', 'a'] """, ) assertEquals(errors.descriptions, ["persistent-workspaces: Contains duplicated workspace names"]) } func testPersistentWorkspacesAreAvailableOnlySinceVersion2() { let (_, errors) = parseConfig( """ persistent-workspaces = ['a'] """, ) assertEquals(errors.descriptions, ["persistent-workspaces: This config option is only available since \'config-version = 2\'"]) } func testQueryCantBeUsedInConfig() { let (_, errors) = parseConfig( """ [mode.main.binding] alt-a = 'list-apps' """, ) XCTAssertTrue(errors.descriptions.singleOrNil()?.contains("cannot be used in config") == true) } func testDropBindings() { let (config, errors) = parseConfig( """ mode.main = {} """, ) assertEquals(errors, []) XCTAssertTrue(config.modes[mainModeId]?.bindings.isEmpty == true) } func testParseMode() { let (config, errors) = parseConfig( """ [mode.main.binding] alt-h = 'focus left' """, ) assertEquals(errors, []) let binding = HotkeyBinding(.option, .h, [FocusCommand.new(direction: .left)]) assertEquals( config.modes[mainModeId], Mode(bindings: [binding.descriptionWithKeyCode: binding]), ) } func testModesMustContainDefaultModeError() { let (config, errors) = parseConfig( """ [mode.foo.binding] alt-h = 'focus left' """, ) assertEquals( errors.descriptions, ["mode: Please specify \'main\' mode"], ) assertEquals(config.modes[mainModeId], nil) } func testHotkeyParseError() { let (config, errors) = parseConfig( """ [mode.main.binding] alt-hh = 'focus left' aalt-j = 'focus down' alt-k = 'focus up' """, ) assertEquals( errors.descriptions, [ "mode.main.binding.aalt-j: Can\'t parse modifiers in \'aalt-j\' binding", "mode.main.binding.alt-hh: Can\'t parse the key in \'alt-hh\' binding", ], ) let binding = HotkeyBinding(.option, .k, [FocusCommand.new(direction: .up)]) assertEquals( config.modes[mainModeId], Mode(bindings: [binding.descriptionWithKeyCode: binding]), ) } func testPermanentWorkspaceNames() { let (config, errors) = parseConfig( """ [mode.main.binding] alt-1 = 'workspace 1' alt-2 = 'workspace 2' alt-3 = ['workspace 3'] alt-4 = ['workspace 4', 'focus left'] """, ) assertEquals(errors.descriptions, []) assertEquals(config.persistentWorkspaces.sorted(), ["1", "2", "3", "4"]) } func testUnknownTopLevelKeyParseError() { let (config, errors) = parseConfig( """ unknownKey = true enable-normalization-flatten-containers = false """, ) assertEquals( errors.descriptions, ["unknownKey: Unknown top-level key"], ) assertEquals(config.enableNormalizationFlattenContainers, false) } func testUnknownKeyParseError() { let (config, errors) = parseConfig( """ enable-normalization-flatten-containers = false [gaps] unknownKey = true """, ) assertEquals( errors.descriptions, ["gaps.unknownKey: Unknown key"], ) assertEquals(config.enableNormalizationFlattenContainers, false) } func testTypeMismatch() { let (_, errors) = parseConfig( """ enable-normalization-flatten-containers = 'true' """, ) assertEquals( errors.descriptions, ["enable-normalization-flatten-containers: Expected type is \'bool\'. But actual type is \'string\'"], ) } func testTomlParseError() { let (_, errors) = parseConfig("true") assertEquals( errors.descriptions, ["Error while parsing key-value pair: encountered end-of-file (at line 1, column 5)"], ) } func testMoveWorkspaceToMonitorCommandParsing() { XCTAssertTrue(parseCommand("move-workspace-to-monitor --wrap-around next").cmdOrNil is MoveWorkspaceToMonitorCommand) XCTAssertTrue(parseCommand("move-workspace-to-display --wrap-around next").cmdOrNil is MoveWorkspaceToMonitorCommand) } func testParseTiles() { let command = parseCommand("layout tiles h_tiles v_tiles list h_list v_list").cmdOrNil XCTAssertTrue(command is LayoutCommand) assertEquals((command as! LayoutCommand).args.toggleBetween.val, [.tiles, .h_tiles, .v_tiles, .tiles, .h_tiles, .v_tiles]) guard case .help = parseCommand("layout tiles -h") else { XCTFail() return } } func testSplitCommandAndFlattenContainersNormalization() { let (_, errors) = parseConfig( """ enable-normalization-flatten-containers = true [mode.main.binding] [mode.foo.binding] alt-s = 'split horizontal' """, ) assertEquals( errors.descriptions, [""" 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'. """], ) } func testParseWorkspaceToMonitorAssignment() { let (parsed, errors) = parseConfig( """ [workspace-to-monitor-force-assignment] workspace_name_1 = 1 # Sequence number of the monitor (from left to right, 1-based indexing) workspace_name_2 = 'main' # main monitor workspace_name_3 = 'secondary' # non-main monitor (in case when there are only two monitors) workspace_name_4 = 'built-in' # case insensitive regex substring workspace_name_5 = '^built-in retina display$' # case insensitive regex match workspace_name_6 = ['secondary', 1] # you can specify multiple patterns. The first matching pattern will be used 7 = "foo" w7 = ['', 'main'] w8 = 0 workspace_name_x = '2' # Sequence number of the monitor (from left to right, 1-based indexing) """, ) assertEquals( parsed.workspaceToMonitorForceAssignment, [ "workspace_name_1": [.sequenceNumber(1)], "workspace_name_2": [.main], "workspace_name_3": [.secondary], "workspace_name_4": [.caseSensitivePattern("built-in")!], "workspace_name_5": [.caseSensitivePattern("^built-in retina display$")!], "workspace_name_6": [.secondary, .sequenceNumber(1)], "workspace_name_x": [.sequenceNumber(2)], "7": [.caseSensitivePattern("foo")!], "w7": [.main], "w8": [], ], ) assertEquals([ "workspace-to-monitor-force-assignment.w7[0]: Empty string is an illegal monitor description", "workspace-to-monitor-force-assignment.w8: Monitor sequence numbers uses 1-based indexing. Values less than 1 are illegal", ], errors.descriptions) assertEquals([:], defaultConfig.workspaceToMonitorForceAssignment) } func testParseOnWindowDetected() { let (parsed, errors) = parseConfig( """ [[on-window-detected]] # 0 check-further-callbacks = true run = ['layout floating', 'move-node-to-workspace W'] [[on-window-detected]] # 1 if.app-id = 'com.apple.systempreferences' run = [] [[on-window-detected]] # 2 [[on-window-detected]] # 3 run = ['move-node-to-workspace S', 'layout tiling'] [[on-window-detected]] # 4 run = ['move-node-to-workspace S', 'move-node-to-workspace W'] [[on-window-detected]] # 5 run = ['move-node-to-workspace S', 'layout h_tiles'] """, ) assertEquals(parsed.onWindowDetected, [ WindowDetectedCallback( // 0 matcher: WindowDetectedCallbackMatcher( appId: nil, appNameRegexSubstring: nil, windowTitleRegexSubstring: nil, ), checkFurtherCallbacks: true, rawRun: [ LayoutCommand(args: LayoutCmdArgs(rawArgs: [], toggleBetween: [.floating])), MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "W")), ], ), WindowDetectedCallback( // 1 matcher: WindowDetectedCallbackMatcher( appId: "com.apple.systempreferences", appNameRegexSubstring: nil, windowTitleRegexSubstring: nil, ), rawRun: [], ), WindowDetectedCallback( // 3 rawRun: [ MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "S")), LayoutCommand(args: LayoutCmdArgs(rawArgs: [], toggleBetween: [.tiling])), ], ), WindowDetectedCallback( // 4 rawRun: [ MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "S")), MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "W")), ], ), WindowDetectedCallback( // 5 rawRun: [ MoveNodeToWorkspaceCommand(args: MoveNodeToWorkspaceCmdArgs(workspace: "S")), LayoutCommand(args: LayoutCmdArgs(rawArgs: [], toggleBetween: [.h_tiles])), ], ), ]) assertEquals(errors.descriptions, [ "on-window-detected[2]: \'run\' is mandatory key", ]) } func testParseOnWindowDetectedRegex() { let (config, errors) = parseConfig( """ [[on-window-detected]] if.app-name-regex-substring = '^system settings$' run = [] """, ) XCTAssertTrue(config.onWindowDetected.singleOrNil()!.matcher.appNameRegexSubstring != nil) assertEquals(errors, []) } func testRegex() { var devNull: [String] = [] XCTAssertTrue("System Settings".contains(parseCaseInsensitiveRegex("settings").getOrNil(appendErrorTo: &devNull)!)) XCTAssertTrue(!"System Settings".contains(parseCaseInsensitiveRegex("^settings^").getOrNil(appendErrorTo: &devNull)!)) } func testParseGaps() { let (config, errors1) = parseConfig( """ [gaps] inner.horizontal = 10 inner.vertical = [{ monitor."main" = 1 }, { monitor."secondary" = 2 }, 5] outer.left = 12 outer.bottom = 13 outer.top = [{ monitor."built-in" = 3 }, { monitor."secondary" = 4 }, 6] outer.right = [{ monitor.2 = 7 }, 8] """, ) assertEquals(errors1, []) assertEquals( config.gaps, Gaps( inner: .init( vertical: .perMonitor( [PerMonitorValue(description: .main, value: 1), PerMonitorValue(description: .secondary, value: 2)], default: 5, ), horizontal: .constant(10), ), outer: .init( left: .constant(12), bottom: .constant(13), top: .perMonitor( [ PerMonitorValue(description: .caseSensitivePattern("built-in")!, value: 3), PerMonitorValue(description: .secondary, value: 4), ], default: 6, ), right: .perMonitor([PerMonitorValue(description: .sequenceNumber(2), value: 7)], default: 8), ), ), ) let (_, errors2) = parseConfig( """ [gaps] inner.horizontal = [true] inner.vertical = [{ foo.main = 1 }, { monitor = { foo = 2, bar = 3 } }, 1] """, ) assertEquals(errors2.descriptions, [ "gaps.inner.horizontal: The last item in the array must be of type Int", "gaps.inner.vertical[0]: The table is expected to have a single key \'monitor\'", "gaps.inner.vertical[1].monitor: The table is expected to have a single key", ]) } func testParseKeyMapping() { let (config, errors) = parseConfig( """ [key-mapping.key-notation-to-key-code] q = 'q' unicorn = 'u' [mode.main.binding] alt-unicorn = 'workspace wonderland' """, ) assertEquals(errors.descriptions, []) assertEquals(config.keyMapping, KeyMapping(preset: .qwerty, rawKeyNotationToKeyCode: [ "q": .q, "unicorn": .u, ])) let binding = HotkeyBinding(.option, .u, [WorkspaceCommand(args: WorkspaceCmdArgs(target: .direct(.parse("unicorn").getOrDie())))]) assertEquals(config.modes[mainModeId]?.bindings, [binding.descriptionWithKeyCode: binding]) let (_, errors1) = parseConfig( """ [key-mapping.key-notation-to-key-code] q = 'qw' ' f' = 'f' """, ) assertEquals(errors1.descriptions, [ "key-mapping.key-notation-to-key-code: ' f' is invalid key notation", "key-mapping.key-notation-to-key-code.q: 'qw' is invalid key code", ]) let (dvorakConfig, dvorakErrors) = parseConfig( """ key-mapping.preset = 'dvorak' """, ) assertEquals(dvorakErrors, []) assertEquals(dvorakConfig.keyMapping, KeyMapping(preset: .dvorak, rawKeyNotationToKeyCode: [:])) assertEquals(dvorakConfig.keyMapping.resolve()["quote"], .q) let (colemakConfig, colemakErrors) = parseConfig( """ key-mapping.preset = 'colemak' """, ) assertEquals(colemakErrors, []) assertEquals(colemakConfig.keyMapping, KeyMapping(preset: .colemak, rawKeyNotationToKeyCode: [:])) assertEquals(colemakConfig.keyMapping.resolve()["f"], .e) } } ================================================ FILE: Sources/AppBundleTests/config/ParseEnvVariablesTest.swift ================================================ @testable import AppBundle import Common import XCTest @MainActor final class ParseEnvVariablesTest: XCTestCase { func testInterpolation() { testSucInterpolation("echo ${foo}", ["foo": "bar"], expected: "echo bar") testSucInterpolation("echo $foo", expected: "echo $foo") testSucInterpolation("echo $$foo", expected: "echo $$foo") testSucInterpolation("echo $${foo}", ["foo": "bar"], expected: "echo $bar") testSucInterpolation("echo $", expected: "echo $") testFailInterpolation("echo ${foo") testFailInterpolation("echo ${foo{bar}") testFailInterpolation("echo ${foo$bar}") testFailInterpolation("echo ${foo}") } func testInherit() { let (config1, errors1) = parseConfig("exec.inherit-env-vars = false") assertEquals(errors1, []) assertEquals(config1.execConfig.envVariables, [:]) let (config2, errors2) = parseConfig("exec.inherit-env-vars = true") assertEquals(errors2, []) assertEquals(config2.execConfig.envVariables, testEnv) } func testAddVars() { let (config, errors) = parseConfig( """ [exec.env-vars] FOO = 'BAR' """, ) assertEquals(errors, []) assertEquals(config.execConfig.envVariables, testEnv + ["FOO": "BAR"]) } func testCyclicDep() { let (_, errors) = parseConfig( """ [exec.env-vars] FOO = '${BAR}' BAR = '${FOO}' """, ) assertEquals(errors.descriptions, [ "exec.env-vars.BAR: Env variable 'FOO' isn't presented in AeroSpace.app env vars, or not available for interpolation (because it's mutated)", "exec.env-vars.FOO: Env variable 'BAR' isn't presented in AeroSpace.app env vars, or not available for interpolation (because it's mutated)", ]) } func testForbidPwd() { let (_, errors) = parseConfig( """ [exec.env-vars] PWD = '' """, ) assertEquals(errors.descriptions, ["exec.env-vars.PWD: Changing 'PWD' is not allowed"]) } } private func testSucInterpolation(_ str: String, _ vars: [String: String] = [:], expected: String) { switch str.interpolate(with: vars) { case .success(let actual): assertEquals(actual, expected) case .failure(let actual): assertEquals(actual, []) } } private func testFailInterpolation(_ str: String, _ vars: [String: String] = [:]) { switch str.interpolate(with: vars) { case .success(let actual): failExpectedActual(nil, actual) case .failure: break } } ================================================ FILE: Sources/AppBundleTests/config/SplitArgsTest.swift ================================================ @testable import AppBundle import Common import XCTest final class SplitArgsTest: XCTestCase { func testSplit() { testSucSplit("echo foo", expected: ["echo", "foo"]) testSucSplit("echo 'foo'", expected: ["echo", "foo"]) testSucSplit("'echo' foo", expected: ["echo", "foo"]) testSucSplit("echo \"'\"", expected: ["echo", "'"]) testSucSplit("echo '\"'", expected: ["echo", "\""]) testSucSplit(" echo ' foo bar'", expected: ["echo", " foo bar"]) testFailSplit("echo 'foo") testFailSplit("echo foo'") } } private func testSucSplit(_ str: String, expected: [String]) { let result = str.splitArgs() switch result { case .success(let actual): assertEquals(actual, expected) case .failure: XCTFail("\(str) split is not successful") } } private func testFailSplit(_ str: String) { let result = str.splitArgs() switch result { case .success: XCTFail("\(str) is expected to fail to split") case .failure: break } } ================================================ FILE: Sources/AppBundleTests/model/ClientServerTest.swift ================================================ @testable import AppBundle import Common import XCTest final class ClientServerTest: XCTestCase { func testClientRequestJsonV1_decoding() { let data = """ { "command": "deprecated", "args": ["foo", "bar"], "stdin": "stdin" } """.data(using: .utf8)! let expected = ClientRequest(args: ["foo", "bar"], stdin: "stdin", windowId: nil, workspace: nil) .copy(\.windowId, nil) .copy(\.workspace, nil) assertSucc(ClientRequest.decodeJson(data), expected) } func testClientRequestJsonV2_decoding() { let data = """ { "args": ["foo", "bar"], "stdin": "stdin" } """.data(using: .utf8)! let expected = ClientRequest(args: ["foo", "bar"], stdin: "stdin", windowId: nil, workspace: nil) .copy(\.windowId, nil) .copy(\.workspace, nil) assertSucc(ClientRequest.decodeJson(data), expected) } func testClientRequestJsonV3_decoding() { let data = """ { "args": ["foo", "bar"], "stdin": "stdin", "windowId": null, "workspace": null } """.data(using: .utf8)! let expected = ClientRequest(args: ["foo", "bar"], stdin: "stdin", windowId: nil, workspace: nil) assertSucc(ClientRequest.decodeJson(data), expected) } func testClientRequestJsonV3_decoding2() { let data = """ { "args": ["foo", "bar"], "stdin": "stdin", "windowId": 1, "workspace": "foo" } """.data(using: .utf8)! let expected = ClientRequest(args: ["foo", "bar"], stdin: "stdin", windowId: 1, workspace: "foo") assertSucc(ClientRequest.decodeJson(data), expected) } func testClientRequestJsonV9999_decoding() { let data = """ { "args": ["foo", "bar"], "stdin": "stdin", "yet another future field": 1 } """.data(using: .utf8)! assertSucc(ClientRequest.decodeJson(data)) } func testClientRequestJsonCompatibility_encoding() { let encoder = JSONEncoder() encoder.outputFormatting = [.sortedKeys] let testData = [ (ClientRequest(args: ["args"], stdin: "stdin", windowId: 0, workspace: "foo"), """ {"args":["args"],"stdin":"stdin","windowId":0,"workspace":"foo"} """), (ClientRequest(args: ["args"], stdin: "stdin", windowId: nil, workspace: nil), """ {"args":["args"],"stdin":"stdin","windowId":null,"workspace":null} """), ] for (req, expectedJson) in testData { let data = try! encoder.encode(req) let str = String.init(data: data, encoding: .utf8)! assertEquals(str, expectedJson) } } func testServerEventEncoding() { let encoder = JSONEncoder() encoder.outputFormatting = [.sortedKeys] let testData: [(ServerEvent, String)] = [ (.focusChanged(windowId: 123, workspace: "1"), #"{"_event":"focus-changed","windowId":123,"workspace":"1"}"#), (.focusedMonitorChanged(workspace: "2", monitorId_oneBased: 1), #"{"_event":"focused-monitor-changed","monitorId":1,"workspace":"2"}"#), (.workspaceChanged(workspace: "2", prevWorkspace: "1"), #"{"_event":"focused-workspace-changed","prevWorkspace":"1","workspace":"2"}"#), (.modeChanged(mode: "resize"), #"{"_event":"mode-changed","mode":"resize"}"#), (.windowDetected(windowId: 456, workspace: "1", appBundleId: "com.example", appName: "Example"), #"{"_event":"window-detected","appBundleId":"com.example","appName":"Example","windowId":456,"workspace":"1"}"#), (.bindingTriggered(mode: "main", binding: "alt-h"), #"{"_event":"binding-triggered","binding":"alt-h","mode":"main"}"#), ] for (event, expectedJson) in testData { let data = try! encoder.encode(event) let str = String(data: data, encoding: .utf8)! assertEquals(str, expectedJson) } } func testServerEventDecoding() { let testData: [(String, ServerEventType)] = [ (#"{"_event":"focus-changed","windowId":123,"workspace":"1","monitorId":1}"#, .focusChanged), (#"{"_event":"focused-monitor-changed","workspace":"2","monitorId":1}"#, .focusedMonitorChanged), (#"{"_event":"focused-workspace-changed","workspace":"2","prevWorkspace":"1"}"#, .workspaceChanged), (#"{"_event":"mode-changed","mode":"resize"}"#, .modeChanged), (#"{"_event":"window-detected","windowId":456}"#, .windowDetected), (#"{"_event":"binding-triggered","mode":"main","binding":"alt-h"}"#, .bindingTriggered), ] for (json, expectedEventType) in testData { let data = json.data(using: .utf8)! let event = try! JSONDecoder().decode(ServerEvent.self, from: data) assertEquals(event.eventType, expectedEventType) } } } ================================================ FILE: Sources/AppBundleTests/shell/ShellTest.swift ================================================ // todo do something about ShellTest. They are commented out to reduce test log verbosity // @testable import AppBundle // import Common // import XCTest // // final class ShellTest: XCTestCase { // let a = cmd("a") // let b = cmd("b") // let c = cmd("c") // let d = cmd("d") // let e = cmd("e") // let f = cmd("f") // let g = cmd("g") // let k = cmd("k") // let backslash = "\\" // let space = " " // // func testParse() { // assertSucc("echo \"foo \\\" bar \(backslash)\(backslash)\(backslash)\(backslash)\" bar".parseShell(), cmd("echo", "foo \" bar \(backslash)\(backslash)", "bar")) // assertSucc(" \n".parseShell(), .empty) // assertSucc("a | b && c | d".parseShell(), .and(.pipe(a, b), .pipe(c, d))) // assertSucc("foo && bar || a && baz".parseShell(), .or(.and(cmd("foo"), cmd("bar")), .and(cmd("a"), cmd("baz")))) // assertSucc("foo a b; bar duh\n baz bro".parseShell(), .seqV(cmd("foo", "a", "b"), cmd("bar", "duh"), cmd("baz", "bro"))) // assertSucc("(a || b) && (c || d)".parseShell(), .and(.or(a, b), .or(c, d))) // assertSucc(""" // a # comment 1 // b && c # comment 2 // d; # comment 3 // """.parseShell(), .seqV(a, .and(b, c), d)) // assertEquals(""" // a && b # comment 1 // # comment 2 // # comment 3 // || c && d // """.parseShell(), .success(.or(.and(a, b), .and(c, d)))) // assertSucc(""" // a \(backslash)\(space) // b c \(backslash) # comment 2 // d && e \(backslash) // && f // """.parseShell(), .and(.and(cmd("a", "b", "c", "d"), e), f)) // assertSucc( // """ // echo "hi \\n $(foo bar)" // """.parseShell(), // .args([.text("echo"), .concatV(.text("hi \n "), .interpolation(cmd("foo", "bar")))]), // ) // assertSucc("echo \"\\n\\t\\$\"".parseShell(), cmd("echo", "\n\t$")) // assertSucc("echo 'single quoted \\n'".parseShell(), cmd("echo", "single quoted \\n")) // // assertFail("echo \"\\f\"".parseShell(), "ERROR: ERROR: Unknown ESCAPE_SEQUENCE '\\f'") // assertFail("echo <".parseShell(), "ERROR: Syntax error at 1:5 extraneous input '<' expecting . Please put the character/word in quotes, if you want to use it as an argument") // assertFail("echo `".parseShell(), "ERROR: Syntax error at 1:5 extraneous input '`' expecting . Please put the character/word in quotes, if you want to use it as an argument") // assertFail("echo \"\"\"\"".parseShell(), "ERROR: Triple quotes are reserved for future use. Please put spaces in between if you meant separate args") // assertFail("echo do".parseShell(), "ERROR: Syntax error at 1:5 extraneous input 'do' expecting . DO is a reserved keyword. Please, put quotes around it, if you want to use it as an argument") // assertFail("echo \"foo \(backslash)\"".parseShell(), "ERROR: Syntax error at 1:12 Unbalanced quotes") // assertFail("a; (b".parseShell(), "ERROR: Syntax error at 1:5 Unbalanced parenthesis") // assertFail("a; (b))".parseShell(), "ERROR: Syntax error at 1:7 Unbalanced parenthesis") // assertFail("|| foo".parseShell()) // assertFail("a && (b || c) foo".parseShell()) // } // // func testParseIf() { // assertSucc("if a then b else c end".parseShell(), .ifElse((a, b), elseB: c)) // assertSucc(""" // if a && b then // c // else // d // end // """.parseShell(), .ifElse((.and(a, b), c), elseB: d)) // assertSucc(""" // if a && b then // c // end // """.parseShell(), .ifElse((.and(a, b), c), elseB: nil)) // assertSucc(""" // if a && b then // c // elif d then // e // elif f then // g // end // """.parseShell(), .ifElse((.and(a, b), c), (d, e), (f, g), elseB: nil)) // assertSucc(""" // if a && b then // end // """.parseShell(), .ifElse((.and(a, b), nil), elseB: nil)) // assertSucc(""" // if a && b then // else // c // end // """.parseShell(), .ifElse((.and(a, b), nil), elseB: c)) // assertSucc(""" // echo foo // // echo bar;; // // if a && b then // c // elif d then // e // elif f then // g // k // end // """.parseShell(), .seqV(cmd("echo", "foo"), cmd("echo", "bar"), .ifElse((.and(a, b), c), (d, e), (f, .seqV(g, k)), elseB: nil))) // } // } // // func cmd(_ args: String...) -> Shell { .args(args.map(ShellString.text)) } // extension Shell { // static func seqV(_ seq: Shell...) -> Shell { .seq(seq) } // } // // extension ShellString { // static func concatV(_ fragments: ShellString...) -> ShellString { .concat(fragments) } // } ================================================ FILE: Sources/AppBundleTests/testExtensions.swift ================================================ @testable import AppBundle import Common import Foundation import TOMLKit extension [TomlParseError] { var descriptions: [String] { map(\.description) } } ================================================ FILE: Sources/AppBundleTests/testUtil.swift ================================================ @testable import AppBundle import Common import Foundation import HotKey import XCTest let projectRoot: URL = { var url = URL(filePath: #filePath).absoluteURL check(FileManager.default.fileExists(atPath: url.path)) while !FileManager.default.fileExists(atPath: url.appending(component: ".git").path) { url.deleteLastPathComponent() } return url }() @MainActor func setUpWorkspacesForTests() { config = defaultConfig configUrl = defaultConfigUrl config.enableNormalizationFlattenContainers = false // Make layout tests more predictable config.enableNormalizationOppositeOrientationForNestedContainers = false // Make layout tests more predictable config.defaultRootContainerOrientation = .horizontal // Make default layout predictable // Don't create any bindings and workspaces for tests config.modes = [mainModeId: Mode(bindings: [:])] config.persistentWorkspaces = [] for workspace in Workspace.all { for child in workspace.children { child.unbindFromParent() } } check(Workspace.get(byName: "setUpWorkspacesForTests").focusWorkspace()) Workspace.garbageCollectUnusedWorkspaces() check(focus.workspace.isEffectivelyEmpty) check(focus.workspace === Workspace.all.singleOrNil(), Workspace.all.map(\.description).joined(separator: ", ")) check(mainMonitor.setActiveWorkspace(focus.workspace)) TestApp.shared.focusedWindow = nil TestApp.shared.windows = [] } extension ParsedCmd { var errorOrNil: String? { if case .failure(let e) = self { return e } else { return nil } } var cmdOrDie: T { cmdOrNil ?? dieT() } } func testParseCommandFail(_ command: String, msg expected: String) { let parsed = parseCommand(command) switch parsed { case .cmd(let command): XCTFail("\(command) isn't supposed to be parcelable") case .failure(let msg): assertEquals(msg, expected) case .help: die() // todo test help } } extension WorkspaceCmdArgs { init(target: WorkspaceTarget, autoBackAndForth: Bool? = nil, wrapAround: Bool? = nil) { self = WorkspaceCmdArgs(rawArgs: []) self.target = .initialized(target) self._autoBackAndForth = autoBackAndForth self._wrapAround = wrapAround } } extension MoveNodeToWorkspaceCmdArgs { init(target: WorkspaceTarget, wrapAround: Bool? = nil) { self = MoveNodeToWorkspaceCmdArgs(rawArgs: []) self.target = .initialized(target) self._wrapAround = wrapAround } init(workspace: String) { self = MoveNodeToWorkspaceCmdArgs(rawArgs: []) self.target = .initialized(.direct(.parse(workspace).getOrDie())) } } extension HotkeyBinding { init(_ modifiers: NSEvent.ModifierFlags, _ keyCode: Key, _ commands: [any Command]) { let descriptionWithKeyNotation = modifiers.isEmpty ? keyCode.toString() : modifiers.toString() + "-" + keyCode.toString() self.init(modifiers, keyCode, commands, descriptionWithKeyNotation: descriptionWithKeyNotation) } } ================================================ FILE: Sources/AppBundleTests/tree/TestApp.swift ================================================ @testable import AppBundle import Common final class TestApp: AbstractApp { let pid: Int32 let rawAppBundleId: String? let name: String? let execPath: String? = nil let bundlePath: String? = nil @MainActor static let shared = TestApp() private init() { self.pid = 0 self.rawAppBundleId = "bobko.AeroSpace.test-app" self.name = rawAppBundleId } var _windows: [Window] = [] var windows: [Window] { get { _windows } set { if let focusedWindow { check(newValue.contains(focusedWindow)) } _windows = newValue } } private var _focusedWindow: Window? = nil var focusedWindow: Window? { get { _focusedWindow } set { if let window = newValue { check(windows.contains(window)) } _focusedWindow = newValue } } @MainActor func getFocusedWindow() -> Window? { _focusedWindow } } ================================================ FILE: Sources/AppBundleTests/tree/TestWindow.swift ================================================ @testable import AppBundle import AppKit final class TestWindow: Window, CustomStringConvertible { private var _rect: Rect? @MainActor private init(_ id: UInt32, _ parent: NonLeafTreeNodeObject, _ adaptiveWeight: CGFloat, _ rect: Rect?) { _rect = rect super.init(id: id, TestApp.shared, lastFloatingSize: nil, parent: parent, adaptiveWeight: adaptiveWeight, index: INDEX_BIND_LAST) } @discardableResult @MainActor static func new(id: UInt32, parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat = 1, rect: Rect? = nil) -> TestWindow { let wi = TestWindow(id, parent, adaptiveWeight, rect) TestApp.shared._windows.append(wi) return wi } nonisolated var description: String { "TestWindow(\(windowId))" } @MainActor override func nativeFocus() { appForTests = TestApp.shared TestApp.shared.focusedWindow = self } override func closeAxWindow() { unbindFromParent() } override var title: String { get async { // redundant async. todo create bug report to Swift description } } @MainActor override func getAxRect() async throws -> Rect? { // todo change to not Optional _rect } } ================================================ FILE: Sources/AppBundleTests/tree/TilingContainer.swift ================================================ @testable import AppBundle import AppKit extension TilingContainer { @MainActor static func newHTiles(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat) -> TilingContainer { newHTiles(parent: parent, adaptiveWeight: adaptiveWeight, index: INDEX_BIND_LAST) } @MainActor static func newVTiles(parent: NonLeafTreeNodeObject, adaptiveWeight: CGFloat) -> TilingContainer { newVTiles(parent: parent, adaptiveWeight: adaptiveWeight, index: INDEX_BIND_LAST) } } ================================================ FILE: Sources/AppBundleTests/tree/TreeNodeTest.swift ================================================ @testable import AppBundle import XCTest @MainActor final class TreeNodeTest: XCTestCase { override func setUp() async throws { setUpWorkspacesForTests() } func testChildParentCyclicReferenceMemoryLeak() { let workspace = Workspace.get(byName: name) // Don't cache root node let window = TestWindow.new(id: 1, parent: workspace.rootTilingContainer) XCTAssertTrue(window.parent != nil) workspace.rootTilingContainer.unbindFromParent() XCTAssertTrue(window.parent == nil) } func testIsEffectivelyEmpty() { let workspace = Workspace.get(byName: name) XCTAssertTrue(workspace.isEffectivelyEmpty) weak let window: TestWindow? = .new(id: 1, parent: workspace.rootTilingContainer) XCTAssertNotEqual(window, nil) XCTAssertTrue(!workspace.isEffectivelyEmpty) window!.unbindFromParent() XCTAssertTrue(workspace.isEffectivelyEmpty) // Don't save to local variable TestWindow.new(id: 2, parent: workspace.rootTilingContainer) XCTAssertTrue(!workspace.isEffectivelyEmpty) } func testNormalizeContainers_dontRemoveRoot() { let workspace = Workspace.get(byName: name) weak let root = workspace.rootTilingContainer func test() { XCTAssertNotEqual(root, nil) XCTAssertTrue(root!.isEffectivelyEmpty) workspace.normalizeContainers() XCTAssertNotEqual(root, nil) } test() config.enableNormalizationFlattenContainers = true test() } func testNormalizeContainers_singleWindowChild() { config.enableNormalizationFlattenContainers = true let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { TestWindow.new(id: 0, parent: $0) TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 1, parent: $0) } } workspace.normalizeContainers() assertEquals( .h_tiles([.window(0), .window(1)]), workspace.rootTilingContainer.layoutDescription, ) } func testNormalizeContainers_removeEffectivelyEmpty() { let workspace = Workspace.get(byName: name) workspace.rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { _ = TilingContainer.newHTiles(parent: $0, adaptiveWeight: 1) } } assertEquals(workspace.rootTilingContainer.children.count, 1) workspace.normalizeContainers() assertEquals(workspace.rootTilingContainer.children.count, 0) } func testNormalizeContainers_flattenContainers() { let workspace = Workspace.get(byName: name) // Don't cache root node workspace.rootTilingContainer.apply { TilingContainer.newVTiles(parent: $0, adaptiveWeight: 1).apply { TestWindow.new(id: 1, parent: $0, adaptiveWeight: 1) } } workspace.normalizeContainers() XCTAssertTrue(workspace.rootTilingContainer.children.singleOrNil() is TilingContainer) config.enableNormalizationFlattenContainers = true workspace.normalizeContainers() XCTAssertTrue(workspace.rootTilingContainer.children.singleOrNil() is TestWindow) } } ================================================ FILE: Sources/Cli/_main.swift ================================================ import Common import Darwin import Foundation import Network let usage = """ USAGE: \(CommandLine.arguments.first ?? "aerospace") [-h|--help] [-v|--version] [...] SUBCOMMANDS: \(subcommandDescriptions.sortedBy { $0[0] }.toPaddingTable(columnSeparator: " ").joined(separator: "\n")) """ @main struct Main { static func main() async { let args = CommandLine.arguments.slice(1...) ?? [] if args.isEmpty { exit(1, err: usage) } if args.first == "--help" || args.first == "-h" { exit(0, out: usage) } if args.first == "--version" || args.first == "-v" { let connection = NWConnection(to: NWEndpoint.unix(path: socketPath), using: .tcp) let serverVersionAndHash: String? if await connection.startBlocking().error == nil { let ans = await run(connection, [], stdin: "", windowId: nil, workspace: nil) serverVersionAndHash = ans.serverVersionAndHash } else { serverVersionAndHash = nil } print( """ aerospace CLI client version: \(cliClientVersionAndHash) AeroSpace.app server version: \(serverVersionAndHash ?? "Unknown. The server is not running") """, ) if serverVersionAndHash != nil && cliClientVersionAndHash != serverVersionAndHash { eprint( """ Warning: AeroSpace client/server versions don't match. Possible fixes: - Restart AeroSpace.app (server restart is required after each update) - Reinstall and restart AeroSpace (corrupted installation) """, ) } exit(0) } let parsedArgs: any CmdArgs switch parseCmdArgs(args) { case .cmd(let _parsedArgs): parsedArgs = _parsedArgs case .help(let help): exit(0, out: help) case .failure(let e): exit(1, err: e) } let connection = NWConnection(to: NWEndpoint.unix(path: socketPath), using: .tcp) if let e = await connection.startBlocking().error { exit(1, err: "Can't connect to AeroSpace server. Is AeroSpace.app running?\n\(e.localizedDescription)") } var stdin = "" if (parsedArgs is WorkspaceCmdArgs && (parsedArgs as! WorkspaceCmdArgs).target.val.isRelatve || parsedArgs is MoveNodeToWorkspaceCmdArgs && (parsedArgs as! MoveNodeToWorkspaceCmdArgs).target.val.isRelatve) && hasStdin() { if parsedArgs is WorkspaceCmdArgs && (parsedArgs as! WorkspaceCmdArgs).explicitStdinFlag == nil || parsedArgs is MoveNodeToWorkspaceCmdArgs && (parsedArgs as! MoveNodeToWorkspaceCmdArgs).explicitStdinFlag == nil { exit( 1, err: """ ERROR: Implicit stdin is detected (stdin is not TTY). Implicit stdin was forbidden in AeroSpace v0.20.0. 1. Please supply '--stdin' flag to make stdin explicit and preserve old AeroSpace behavior 2. You can also use '--no-stdin' flag to behave as if no stdin was supplied Breaking change issue: https://github.com/nikitabobko/AeroSpace/issues/1683 """, ) } var index = 0 while let line = readLine(strippingNewline: false) { stdin += line index += 1 if index > 1000 { exit(1, err: "stdin number of lines limit is exceeded") } } } let windowId = ProcessInfo.processInfo.environment[AEROSPACE_WINDOW_ID].flatMap(UInt32.init) let workspace = ProcessInfo.processInfo.environment[AEROSPACE_WORKSPACE] // Handle subscribe command specially if parsedArgs is SubscribeCmdArgs { await runSubscribe(connection, args, windowId: windowId, workspace: workspace) exit(0) // Should not reach here } let ans = await run(connection, args, stdin: stdin, windowId: windowId, workspace: workspace) if !ans.stdout.isEmpty { print(ans.stdout) } if !ans.stderr.isEmpty { eprint(ans.stderr) } if ans.exitCode != 0 && ans.serverVersionAndHash != cliClientVersionAndHash { eprint( """ Warning: AeroSpace client/server versions don't match - aerospace CLI client version: \(cliClientVersionAndHash) - AeroSpace.app server version: \(ans.serverVersionAndHash) Possible fixes: - Restart AeroSpace.app (server restart is required after each update) - Reinstall and restart AeroSpace (corrupted installation) """, ) } exit(ans.exitCode) } } func runSubscribe(_ connection: NWConnection, _ args: StrArrSlice, windowId: UInt32?, workspace: String?) async { if let e = await connection.writeAtomic(ClientRequest(args: args.toArray(), stdin: "", windowId: windowId, workspace: workspace)).error { exit(1, err: "Failed to write to server socket: \(e)") } while true { switch await connection.readNonAtomic() { case .success(let data): if let str = String(data: data, encoding: .utf8) { print(str) fflush(stdout) } else { exit(1, err: "Can't convert bytes to utf8 String") } case .failure(let e): exit(1, err: "runSubscribe error: \(e)") } } } func run(_ connection: NWConnection, _ args: StrArrSlice, stdin: String, windowId: UInt32?, workspace: String?) async -> ServerAnswer { if let e = await connection.writeAtomic(ClientRequest(args: args.toArray(), stdin: stdin, windowId: windowId, workspace: workspace)).error { exit(1, err: "Failed to write to server socket: \(e)") } switch await connection.readNonAtomic() { case .success(let answer): return (try? JSONDecoder().decode(ServerAnswer.self, from: answer)) ?? exitT(1, err: "Failed to parse server response: \(String(data: answer, encoding: .utf8).prettyDescription)") case .failure(let error): exit(1, err: "Failed to read from server socket: \(error)") } } ================================================ FILE: Sources/Cli/cliUtil.swift ================================================ import Common import Darwin import Foundation let cliClientVersionAndHash: String = "\(aeroSpaceAppVersion) \(gitHash)" func hasStdin() -> Bool { isatty(STDIN_FILENO) != 1 } ================================================ FILE: Sources/Cli/subcommandDescriptionsGenerated.swift ================================================ // FILE IS GENERATED FROM docs/aerospace-*.adoc files // TO REGENERATE THE FILE RUN generate.sh let subcommandDescriptions = [ [" balance-sizes", "Balance sizes of all windows in the current workspace"], [" close-all-windows-but-current", "On the focused workspace, close all windows but current"], [" close", "Close the focused window"], [" config", "Query AeroSpace config options"], [" debug-windows", "Interactive command to record Accessibility API debug information to create bug reports"], [" enable", "Temporarily disable window management"], [" flatten-workspace-tree", "Flatten the tree of the focused workspace"], [" focus-back-and-forth", "Switch between the current and previously focused elements back and forth"], [" focus-monitor", "Focus monitor by relative direction, by order, or by pattern"], [" focus", "Set focus to a window."], [" fullscreen", "Toggle the fullscreen mode for the focused window"], [" join-with", "Put the focused window and the nearest node in the specified direction under a common parent container"], [" layout", "Change layout of the focused window to the given layout"], [" list-apps", "Print the list of running applications that appears in the Dock and may have a user interface"], [" list-exec-env-vars", "List environment variables that exec-* commands and callbacks are run with"], [" list-modes", "Print a list of modes currently specified in the configuration"], [" list-monitors", "Print monitors that satisfy conditions"], [" list-windows", "Print windows that satisfy conditions"], [" list-workspaces", "Print workspaces that satisfy conditions"], [" macos-native-fullscreen", "Toggle macOS fullscreen for the focused window"], [" macos-native-minimize", "Minimize focused window"], [" mode", "Activate the specified binding mode"], [" move-mouse", "Move mouse to the requested position"], [" move-node-to-monitor", "Move window to monitor targeted by relative direction, by order, or by pattern"], [" move-node-to-workspace", "Move the focused window to the specified workspace"], [" move-workspace-to-monitor", "Move workspace to monitor targeted by relative direction, by order, or by pattern."], [" move", "Move the focused window in the given direction"], [" reload-config", "Reload currently active config"], [" resize", "Resize the focused window"], [" split", "Split focused window"], [" subscribe", "Subscribe to AeroSpace events and receive notifications via socket"], [" summon-workspace", "Move the requested workspace to the focused monitor."], [" swap", "Swaps the focused window with another window."], [" trigger-binding", "Trigger AeroSpace binding as if it was pressed by user"], [" volume", "Manipulate volume"], [" workspace-back-and-forth", "Switch between the focused workspace and previously focused workspace back and forth"], [" workspace", "Focus the specified workspace"], ] ================================================ FILE: Sources/Common/appMetadata.swift ================================================ public let stableAeroSpaceAppId: String = "bobko.aerospace" #if DEBUG public let aeroSpaceAppId: String = "bobko.aerospace.debug" public let aeroSpaceAppName: String = "AeroSpace-Debug" #else public let aeroSpaceAppId: String = stableAeroSpaceAppId public let aeroSpaceAppName: String = "AeroSpace" #endif ================================================ FILE: Sources/Common/cmdArgs/ArgParser.swift ================================================ public typealias SendableWritableKeyPath = Sendable & WritableKeyPath typealias ArgParserFun = @Sendable (Input) -> ParsedCliArgs protocol ArgParserProtocol: Sendable { associatedtype Input associatedtype Value associatedtype Root associatedtype Context var context: Context { get } var keyPath: SendableWritableKeyPath { get } var parse: ArgParserFun { get } } struct ArgParser: ArgParserProtocol { let keyPath: SendableWritableKeyPath let parse: ArgParserFun let context: Context init( _ keyPath: SendableWritableKeyPath, _ parse: @escaping ArgParserFun, context: Context, ) { self.keyPath = keyPath self.parse = parse self.context = context } init( _ keyPath: SendableWritableKeyPath, _ parse: @escaping ArgParserFun, ) where Context == (), Input == SubArgParserInput { self.init(keyPath, parse, context: ()) } init( _ keyPath: SendableWritableKeyPath, _ parse: @escaping ArgParserFun, argPlaceholderIfMandatory: String? = nil, ) where Context == PosArgParserContext, Input == PosArgParserInput { self.init(keyPath, parse, context: PosArgParserContext(argPlaceholderIfMandatory: argPlaceholderIfMandatory)) } } typealias PosArgParser = ArgParser struct PosArgParserContext { let argPlaceholderIfMandatory: String? } public struct ParsedCliArgs { var value: Parsed var advanceBy: Int public init(_ value: Parsed, advanceBy: Int) { self.value = value self.advanceBy = advanceBy } public static func succ(_ value: T, advanceBy: Int) -> ParsedCliArgs { .init(.success(value), advanceBy: advanceBy) } public static func fail(_ msg: String, advanceBy: Int) -> ParsedCliArgs { .init(.failure(msg), advanceBy: advanceBy) } public func flatMap(_ mapper: (T) -> ParsedCliArgs) -> ParsedCliArgs { switch value { case .failure(let msg): ParsedCliArgs(.failure(msg), advanceBy: advanceBy) case .success(let value): mapper(value) } } public func map(_ mapper: (T) -> R) -> ParsedCliArgs { flatMap { ParsedCliArgs(.success(mapper($0)), advanceBy: advanceBy) } } } func newMandatoryPosArgParser( _ keyPath: SendableWritableKeyPath>, _ parse: @escaping @Sendable (PosArgParserInput) -> ParsedCliArgs, placeholder: String, ) -> PosArgParser> { let parseWrapper: @Sendable (PosArgParserInput) -> ParsedCliArgs> = { parse($0).map { .initialized($0) } } return ArgParser( keyPath, parseWrapper, context: PosArgParserContext(argPlaceholderIfMandatory: placeholder), ) } // todo reuse in config public func parseEnum(_ raw: String, _ _: T.Type) -> Parsed where T.RawValue == String, T: CaseIterable { T(rawValue: raw).orFailure("Can't parse '\(raw)'.\nPossible values: \(T.unionLiteral)") } func parseCardinalDirectionArg(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, CardinalDirection.self), advanceBy: 1) } func parseCardinalOrDfsDirection(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, CardinalOrDfsDirection.self), advanceBy: 1) } func upcastArgParserFun(_ fun: @escaping ArgParserFun) -> ArgParserFun { { fun($0).map { $0 } } } ================================================ FILE: Sources/Common/cmdArgs/ArgParserInput.swift ================================================ struct PosArgParserInput: ArgParserInput { /*conforms*/ let index: Int /*conforms*/ let args: StrArrSlice var arg: String { args[index] } } struct SubArgParserInput: ArgParserInput { let superArg: String /*conforms*/ let index: Int /*conforms*/ let args: StrArrSlice var argOrNil: String? { args.getOrNil(atIndex: index) } } protocol ArgParserInput { var index: Int { get } var args: StrArrSlice { get } } extension ArgParserInput { func getOrNil(relativeIndex i: Int) -> String? { args.getOrNil(atIndex: index + i) } func nonFlagArgs() -> ArrSlice { var i = index while args.indices.contains(i) && !args[i].starts(with: "-") { i += 1 } return args.slice(index ..< i).orDie() } func nonFlagArgOrNil() -> String? { args.getOrNil(atIndex: index)?.takeIf { !$0.starts(with: "-") } } } ================================================ FILE: Sources/Common/cmdArgs/SubArgParser.swift ================================================ typealias SubArgParser = ArgParser func parseUInt32SubArg(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { return .init(UInt32(arg).orFailure("Can't parse '\(arg)'. It must be a positive number"), advanceBy: 1) } else { return .fail("'\(i.superArg)' must be followed by mandatory UInt32", advanceBy: 0) } } func optionalWindowIdFlag() -> SubArgParser { ArgParser(\T.windowId, upcastArgParserFun(parseUInt32SubArg)) } func optionalWorkspaceFlag() -> SubArgParser { ArgParser(\T.workspaceName, upcastArgParserFun(parseWorkspaceNameSubArg)) } func trueBoolFlag(_ keyPath: SendableWritableKeyPath) -> SubArgParser { ArgParser(keyPath) { _ in .succ(true, advanceBy: 0) } } func falseBoolFlag(_ keyPath: SendableWritableKeyPath) -> SubArgParser { ArgParser(keyPath) { _ in .succ(false, advanceBy: 0) } } func boolFlag(_ keyPath: SendableWritableKeyPath) -> SubArgParser { ArgParser(keyPath) { input in input.argOrNil == "no" ? .succ(false, advanceBy: 1) : .succ(true, advanceBy: 0) } } func singleValueSubArgParser( _ keyPath: SendableWritableKeyPath, _ placeholder: String, _ mapper: @escaping @Sendable (String) -> Value?, ) -> SubArgParser { ArgParser(keyPath) { input in if let arg = input.nonFlagArgOrNil() { if let value: Value = mapper(arg) { .succ(value, advanceBy: 1) } else { .fail("Failed to convert '\(arg)' to '\(Value.self)'", advanceBy: 1) } } else { .fail("'\(placeholder)' is mandatory", advanceBy: 0) } } } func optionalTrueBoolFlag(_ keyPath: SendableWritableKeyPath) -> SubArgParser { ArgParser(keyPath) { _ in .succ(true, advanceBy: 0) } } func optionalFalseBoolFlag(_ KeyPath: SendableWritableKeyPath) -> SubArgParser { ArgParser(KeyPath) { _ in .succ(false, advanceBy: 0) } } func parseWorkspaceNameSubArg(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { .init(WorkspaceName.parse(arg), advanceBy: 1) } else { .fail("'\(i.superArg)' must be followed by mandatory workspace name", advanceBy: 0) } } ================================================ FILE: Sources/Common/cmdArgs/cmdArgsManifest.swift ================================================ public enum CmdKind: String, CaseIterable, Equatable, Sendable { // Sorted case balanceSizes = "balance-sizes" case close case closeAllWindowsButCurrent = "close-all-windows-but-current" case config case debugWindows = "debug-windows" case enable case execAndForget = "exec-and-forget" case flattenWorkspaceTree = "flatten-workspace-tree" case focus case focusBackAndForth = "focus-back-and-forth" case focusMonitor = "focus-monitor" case fullscreen case joinWith = "join-with" case layout case listApps = "list-apps" case listExecEnvVars = "list-exec-env-vars" case listModes = "list-modes" case listMonitors = "list-monitors" case listWindows = "list-windows" case listWorkspaces = "list-workspaces" case macosNativeFullscreen = "macos-native-fullscreen" case macosNativeMinimize = "macos-native-minimize" case mode case move = "move" case moveMouse = "move-mouse" case moveNodeToMonitor = "move-node-to-monitor" case moveNodeToWorkspace = "move-node-to-workspace" case moveWorkspaceToMonitor = "move-workspace-to-monitor" case reloadConfig = "reload-config" case resize case split case subscribe case summonWorkspace = "summon-workspace" case swap case triggerBinding = "trigger-binding" case volume case workspace case workspaceBackAndForth = "workspace-back-and-forth" } func initSubcommands() -> [String: any SubCommandParserProtocol] { var result: [String: any SubCommandParserProtocol] = [:] for kind in CmdKind.allCases { switch kind { case .balanceSizes: result[kind.rawValue] = SubCommandParser(BalanceSizesCmdArgs.init) case .close: result[kind.rawValue] = SubCommandParser(CloseCmdArgs.init) case .closeAllWindowsButCurrent: result[kind.rawValue] = SubCommandParser(CloseAllWindowsButCurrentCmdArgs.init) case .config: result[kind.rawValue] = SubCommandParser(parseConfigCmdArgs) case .debugWindows: result[kind.rawValue] = SubCommandParser(DebugWindowsCmdArgs.init) case .enable: result[kind.rawValue] = SubCommandParser(parseEnableCmdArgs) case .execAndForget: break // exec-and-forget is parsed separately case .flattenWorkspaceTree: result[kind.rawValue] = SubCommandParser(FlattenWorkspaceTreeCmdArgs.init) case .focus: result[kind.rawValue] = SubCommandParser(parseFocusCmdArgs) case .focusBackAndForth: result[kind.rawValue] = SubCommandParser(FocusBackAndForthCmdArgs.init) case .focusMonitor: result[kind.rawValue] = SubCommandParser(parseFocusMonitorCmdArgs) case .fullscreen: result[kind.rawValue] = SubCommandParser(parseFullscreenCmdArgs) case .joinWith: result[kind.rawValue] = SubCommandParser(JoinWithCmdArgs.init) case .layout: result[kind.rawValue] = SubCommandParser(parseLayoutCmdArgs) case .listApps: result[kind.rawValue] = SubCommandParser(parseListAppsCmdArgs) case .listExecEnvVars: result[kind.rawValue] = SubCommandParser(ListExecEnvVarsCmdArgs.init) case .listModes: result[kind.rawValue] = SubCommandParser(parseListModesCmdArgs) case .listMonitors: result[kind.rawValue] = SubCommandParser(parseListMonitorsCmdArgs) case .listWindows: result[kind.rawValue] = SubCommandParser(parseListWindowsCmdArgs) case .listWorkspaces: result[kind.rawValue] = SubCommandParser(parseListWorkspacesCmdArgs) case .macosNativeFullscreen: result[kind.rawValue] = SubCommandParser(parseMacosNativeFullscreenCmdArgs) case .macosNativeMinimize: result[kind.rawValue] = SubCommandParser(MacosNativeMinimizeCmdArgs.init) case .mode: result[kind.rawValue] = SubCommandParser(ModeCmdArgs.init) case .move: result[kind.rawValue] = SubCommandParser(parseMoveCmdArgs) // deprecated result["move-through"] = SubCommandParser(parseMoveCmdArgs) case .moveMouse: result[kind.rawValue] = SubCommandParser(parseMoveMouseCmdArgs) case .moveNodeToMonitor: result[kind.rawValue] = SubCommandParser(parseMoveNodeToMonitorCmdArgs) case .moveNodeToWorkspace: result[kind.rawValue] = SubCommandParser(parseMoveNodeToWorkspaceCmdArgs) case .moveWorkspaceToMonitor: result[kind.rawValue] = SubCommandParser(parseWorkspaceToMonitorCmdArgs) // deprecated result["move-workspace-to-display"] = SubCommandParser(MoveWorkspaceToMonitorCmdArgs.init) case .reloadConfig: result[kind.rawValue] = SubCommandParser(ReloadConfigCmdArgs.init) case .resize: result[kind.rawValue] = SubCommandParser(parseResizeCmdArgs) case .split: result[kind.rawValue] = SubCommandParser(parseSplitCmdArgs) case .subscribe: result[kind.rawValue] = SubCommandParser(parseSubscribeCmdArgs) case .summonWorkspace: result[kind.rawValue] = SubCommandParser(SummonWorkspaceCmdArgs.init) case .swap: result[kind.rawValue] = SubCommandParser(parseSwapCmdArgs) case .triggerBinding: result[kind.rawValue] = SubCommandParser(parseTriggerBindingCmdArgs) case .volume: result[kind.rawValue] = SubCommandParser(VolumeCmdArgs.init) case .workspace: result[kind.rawValue] = SubCommandParser(parseWorkspaceCmdArgs) case .workspaceBackAndForth: result[kind.rawValue] = SubCommandParser(WorkspaceBackAndForthCmdArgs.init) } } return result } ================================================ FILE: Sources/Common/cmdArgs/impl/BalanceSizesCmdArgs.swift ================================================ public struct BalanceSizesCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .balanceSizes, allowInConfig: true, help: balance_sizes_help_generated, flags: [ "--workspace": optionalWorkspaceFlag(), ], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/CloseAllWindowsButCurrentCmdArgs.swift ================================================ public struct CloseAllWindowsButCurrentCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .closeAllWindowsButCurrent, allowInConfig: true, help: close_all_windows_but_current_help_generated, flags: [ "--quit-if-last-window": trueBoolFlag(\.closeArgs.quitIfLastWindow), ], posArgs: [], ) public var closeArgs = CloseCmdArgs(rawArgs: []) } ================================================ FILE: Sources/Common/cmdArgs/impl/CloseCmdArgs.swift ================================================ public struct CloseCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .close, allowInConfig: true, help: close_help_generated, flags: [ "--quit-if-last-window": trueBoolFlag(\.quitIfLastWindow), "--window-id": optionalWindowIdFlag(), ], posArgs: [], ) public var quitIfLastWindow: Bool = false } ================================================ FILE: Sources/Common/cmdArgs/impl/ConfigCmdArgs.swift ================================================ public struct ConfigCmdArgs: CmdArgs, Equatable { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .config, allowInConfig: false, help: config_help_generated, flags: [ "--json": trueBoolFlag(\.json), "--keys": trueBoolFlag(\.keys), "--major-keys": trueBoolFlag(\.majorKeys), "--all-keys": trueBoolFlag(\.allKeys), "--config-path": trueBoolFlag(\.configPath), "--get": singleValueSubArgParser(\.keyNameToGet, "") { $0 }, ], posArgs: [], ) public var json: Bool = false public var majorKeys: Bool = false public var keys: Bool = false public var allKeys: Bool = false public var configPath: Bool = false public var keyNameToGet: String? = nil } extension ConfigCmdArgs { public enum Mode { case getKey(key: String), majorKeys, allKeys, configPath } public var mode: Mode { if let keyNameToGet { return .getKey(key: keyNameToGet) } if majorKeys { return .majorKeys } if allKeys { return .allKeys } if configPath { return .configPath } die("At least one mode must be specified") } } func parseConfigCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ConfigCmdArgs(commonState: .init(args)), args) .flatMap { raw in var conflicting: Set = [] if raw.keyNameToGet != nil { conflicting.insert("--get") } if raw.majorKeys { conflicting.insert("--major-keys") } if raw.allKeys { conflicting.insert("--all-keys") } if raw.configPath { conflicting.insert("--config-path") } return switch conflicting.count { case 1: .cmd(raw) case 0: .failure("Mandatory flag is not specified (--get|--major-keys|--all-keys|--config-path)") default: .failure("Conflicting flags are specified: \(conflicting.joined(separator: ", "))") } } .filter("--keys flag requires --get flag") { !$0.keys || $0.keyNameToGet != nil } .filter("--json flag requires --get flag") { !$0.json || $0.keyNameToGet != nil } } ================================================ FILE: Sources/Common/cmdArgs/impl/DebugWindowsCmdArgs.swift ================================================ public struct DebugWindowsCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .debugWindows, allowInConfig: false, help: debug_windows_help_generated, flags: [ "--window-id": ArgParser(\.windowId, upcastArgParserFun(parseUInt32SubArg)), ], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/EnableCmdArgs.swift ================================================ public struct EnableCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .enable, allowInConfig: true, help: enable_help_generated, flags: [ "--fail-if-noop": trueBoolFlag(\.failIfNoop), ], posArgs: [newMandatoryPosArgParser(\.targetState, parseState, placeholder: EnableCmdArgs.State.unionLiteral)], ) public var targetState: Lateinit = .uninitialized public var failIfNoop: Bool = false public init(rawArgs: [String], targetState: State) { self.commonState = .init(rawArgs.slice) self.targetState = .initialized(targetState) } public enum State: String, CaseIterable, Sendable { case on, off, toggle } } func parseEnableCmdArgs(_ args: StrArrSlice) -> ParsedCmd { return parseSpecificCmdArgs(EnableCmdArgs(rawArgs: args), args) .filterNot("--fail-if-noop is incompatible with 'toggle' argument") { $0.targetState.val == .toggle && $0.failIfNoop } } private func parseState(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, EnableCmdArgs.State.self), advanceBy: 1) } ================================================ FILE: Sources/Common/cmdArgs/impl/ExecAndForgetCmdArgs.swift ================================================ public struct ExecAndForgetCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .execAndForget, allowInConfig: true, help: exec_and_forget_help_generated, flags: [:], posArgs: [], ) public init(bashScript: String) { self.commonState = .init([bashScript]) self.bashScript = bashScript } public let bashScript: String } ================================================ FILE: Sources/Common/cmdArgs/impl/FlattenWorkspaceTreeCmdArgs.swift ================================================ public struct FlattenWorkspaceTreeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .flattenWorkspaceTree, allowInConfig: true, help: flatten_workspace_tree_help_generated, flags: [ "--workspace": optionalWorkspaceFlag(), ], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/FocusBackAndForthCmdArgs.swift ================================================ public struct FocusBackAndForthCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .focusBackAndForth, allowInConfig: true, help: focus_back_and_forth_help_generated, flags: [:], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/FocusCmdArgs.swift ================================================ public struct FocusCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .focus, allowInConfig: true, help: focus_help_generated, flags: [ "--ignore-floating": falseBoolFlag(\.floatingAsTiling), "--window-id": ArgParser(\.windowId, upcastArgParserFun(parseUInt32SubArg)), "--dfs-index": ArgParser(\.dfsIndex, upcastArgParserFun(parseUInt32SubArg)), "--boundaries": ArgParser(\.rawBoundaries, upcastArgParserFun(parseBoundaries)), "--boundaries-action": ArgParser(\.rawBoundariesAction, upcastArgParserFun(parseBoundariesAction)), "--wrap-around": trueBoolFlag(\.wrapAroundAlias), ], posArgs: [ArgParser(\.cardinalOrDfsDirection, upcastArgParserFun(parseCardinalOrDfsDirection))], conflictingOptions: [ ["--wrap-around", "--boundaries-action"], ["--wrap-around", "--boundaries"], ], ) public var rawBoundaries: Boundaries? = nil // todo cover boundaries wrapping with tests public var rawBoundariesAction: WhenBoundariesCrossed? = nil fileprivate var wrapAroundAlias: Bool = false public var dfsIndex: UInt32? = nil public var cardinalOrDfsDirection: CardinalOrDfsDirection? = nil public var floatingAsTiling: Bool = true public init(rawArgs: StrArrSlice, cardinalOrDfsDirection: CardinalOrDfsDirection) { self.commonState = .init(rawArgs) self.cardinalOrDfsDirection = cardinalOrDfsDirection } public init(rawArgs: StrArrSlice, windowId: UInt32) { self.commonState = .init(rawArgs) self.windowId = windowId } public init(rawArgs: StrArrSlice, dfsIndex: UInt32) { self.commonState = .init(rawArgs) self.dfsIndex = dfsIndex } public enum Boundaries: String, CaseIterable, Equatable, Sendable { case workspace case allMonitorsOuterFrame = "all-monitors-outer-frame" } public enum WhenBoundariesCrossed: String, CaseIterable, Equatable, Sendable { case stop = "stop" case fail = "fail" case wrapAroundTheWorkspace = "wrap-around-the-workspace" case wrapAroundAllMonitors = "wrap-around-all-monitors" } } public enum FocusCmdTarget { case direction(CardinalDirection) case windowId(UInt32) case dfsIndex(UInt32) case dfsRelative(DfsNextPrev) var isDfsRelative: Bool { if case .dfsRelative = self { return true } else { return false } } } extension FocusCmdArgs { public var target: FocusCmdTarget { if let cardinalOrDfsDirection { return switch cardinalOrDfsDirection { case .direction(let dir): .direction(dir) case .dfsRelative(let nextPrev): .dfsRelative(nextPrev) } } if let windowId { return .windowId(windowId) } if let dfsIndex { return .dfsIndex(dfsIndex) } die("Parser invariants are broken") } public var boundaries: Boundaries { rawBoundaries ?? .workspace } public var boundariesAction: WhenBoundariesCrossed { wrapAroundAlias ? .wrapAroundTheWorkspace : (rawBoundariesAction ?? .stop) } } func parseFocusCmdArgs(_ args: StrArrSlice) -> ParsedCmd { return parseSpecificCmdArgs(FocusCmdArgs(rawArgs: args), args) .flatMap { (raw: FocusCmdArgs) -> ParsedCmd in raw.boundaries == .workspace && raw.boundariesAction == .wrapAroundAllMonitors ? .failure("\(raw.boundaries.rawValue) and \(raw.boundariesAction.rawValue) is an invalid combination of values") : .cmd(raw) } .filter("Mandatory argument is missing. \(CardinalOrDfsDirection.unionLiteral), --window-id or --dfs-index is required") { $0.cardinalOrDfsDirection != nil || $0.windowId != nil || $0.dfsIndex != nil } .filter("--window-id is incompatible with other options") { $0.windowId == nil || $0 == FocusCmdArgs(rawArgs: args, windowId: $0.windowId.orDie()) } .filter("--dfs-index is incompatible with other options") { $0.dfsIndex == nil || $0 == FocusCmdArgs(rawArgs: args, dfsIndex: $0.dfsIndex.orDie()) } .filter("(dfs-next|dfs-prev) only supports --boundaries workspace") { $0.target.isDfsRelative.implies($0.boundaries == .workspace) } } private func parseBoundariesAction(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { return .init(parseEnum(arg, FocusCmdArgs.WhenBoundariesCrossed.self), advanceBy: 1) } else { return .fail(" is mandatory", advanceBy: 0) } } private func parseBoundaries(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { return .init(parseEnum(arg, FocusCmdArgs.Boundaries.self), advanceBy: 1) } else { return .fail(" is mandatory", advanceBy: 0) } } ================================================ FILE: Sources/Common/cmdArgs/impl/FocusMonitorCmdArgs.swift ================================================ public struct FocusMonitorCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .focusMonitor, allowInConfig: true, help: focus_monitor_help_generated, flags: [ "--wrap-around": trueBoolFlag(\.wrapAround), ], posArgs: [newMandatoryPosArgParser(\.target, parseTarget, placeholder: MonitorTarget.cases.joinedCliArgs)], ) public var wrapAround: Bool = false public var target: Lateinit = .uninitialized } func parseFocusMonitorCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(FocusMonitorCmdArgs(rawArgs: args), args) .filter("--wrap-around is incompatible with argument") { !$0.wrapAround || !$0.target.val.isPatterns } } func parseTarget(i: PosArgParserInput) -> ParsedCliArgs { switch i.arg { case "next": return .succ(.relative(.next), advanceBy: 1) case "prev": return .succ(.relative(.prev), advanceBy: 1) case "left": return .succ(.direction(.left), advanceBy: 1) case "down": return .succ(.direction(.down), advanceBy: 1) case "up": return .succ(.direction(.up), advanceBy: 1) case "right": return .succ(.direction(.right), advanceBy: 1) default: let args = i.nonFlagArgs() return .init(args.mapAllOrFailure(parseMonitorDescription).map { .patterns($0) }, advanceBy: args.count) } } public enum MonitorTarget: Equatable, Sendable { case direction(CardinalDirection) case relative(NextPrev) case patterns([MonitorDescription]) var isPatterns: Bool { switch self { case .patterns: true default: false } } static var casesExceptPatterns: [String] { CardinalDirection.cliArgsCases + NextPrev.cliArgsCases } static var cases: [String] { casesExceptPatterns + [""] } public var directionOrNil: CardinalDirection? { switch self { case .direction(let direction): direction default: nil } } } ================================================ FILE: Sources/Common/cmdArgs/impl/FullscreenCmdArgs.swift ================================================ public struct FullscreenCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .fullscreen, allowInConfig: true, help: fullscreen_help_generated, flags: [ "--no-outer-gaps": trueBoolFlag(\.noOuterGaps), "--fail-if-noop": trueBoolFlag(\.failIfNoop), "--window-id": optionalWindowIdFlag(), ], posArgs: [ArgParser(\.toggle, parseToggleEnum)], ) public var toggle: ToggleEnum = .toggle public var noOuterGaps: Bool = false public var failIfNoop: Bool = false } func parseFullscreenCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(FullscreenCmdArgs(rawArgs: args), args) .filterNot("--no-outer-gaps is incompatible with 'off' argument") { $0.toggle == .off && $0.noOuterGaps } .filter("--fail-if-noop requires 'on' or 'off' argument") { $0.failIfNoop.implies($0.toggle == .on || $0.toggle == .off) } } ================================================ FILE: Sources/Common/cmdArgs/impl/JoinWithCmdArgs.swift ================================================ public struct JoinWithCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .joinWith, allowInConfig: true, help: join_with_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), ], posArgs: [newMandatoryPosArgParser(\.direction, parseCardinalDirectionArg, placeholder: CardinalDirection.unionLiteral)], ) public var direction: Lateinit = .uninitialized public init(rawArgs: [String], direction: CardinalDirection) { self.commonState = .init(rawArgs.slice) self.direction = .initialized(direction) } } ================================================ FILE: Sources/Common/cmdArgs/impl/LayoutCmdArgs.swift ================================================ public struct LayoutCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .layout, allowInConfig: true, help: layout_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), ], posArgs: [newMandatoryPosArgParser(\.toggleBetween, parseToggleBetween, placeholder: LayoutDescription.unionLiteral)], ) public var toggleBetween: Lateinit<[LayoutDescription]> = .uninitialized public init(rawArgs: [String], toggleBetween: [LayoutDescription]) { self.commonState = .init(rawArgs.slice) self.toggleBetween = .initialized(toggleBetween) } public enum LayoutDescription: String, CaseIterable, Equatable, Sendable { case accordion, tiles case horizontal, vertical case h_accordion, v_accordion, h_tiles, v_tiles case tiling, floating } } private func parseToggleBetween(input: PosArgParserInput) -> ParsedCliArgs<[LayoutCmdArgs.LayoutDescription]> { let args = input.nonFlagArgs() var result: [LayoutCmdArgs.LayoutDescription] = [] var i = 0 for arg in args { if let layout = arg.parseLayoutDescription() { result.append(layout) } else { return .fail( "Can't parse '\(arg)'\nPossible values: \(LayoutCmdArgs.LayoutDescription.unionLiteral)", advanceBy: i + 1, ) } i += 1 } return .succ(result, advanceBy: args.count) } func parseLayoutCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(LayoutCmdArgs(rawArgs: args), args).map { check(!$0.toggleBetween.val.isEmpty) return $0 } } extension String { fileprivate func parseLayoutDescription() -> LayoutCmdArgs.LayoutDescription? { if let parsed = LayoutCmdArgs.LayoutDescription(rawValue: self) { return parsed } else if self == "list" { return .tiles } else if self == "h_list" { return .h_tiles } else if self == "v_list" { return .v_tiles } return nil } } ================================================ FILE: Sources/Common/cmdArgs/impl/ListAppsCmdArgs.swift ================================================ public struct ListAppsCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .listApps, allowInConfig: false, help: list_apps_help_generated, flags: [ "--macos-native-hidden": boolFlag(\.macosHidden), // Formatting flags "--format": formatParser(\._format, for: .app), "--count": trueBoolFlag(\.outputOnlyCount), "--json": trueBoolFlag(\.json), ], posArgs: [], conflictingOptions: [ ["--count", "--format"], ["--count", "--json"], ], ) public var macosHidden: Bool? public var _format: [StringInterToken] = [] public var outputOnlyCount: Bool = false public var json: Bool = false } extension ListAppsCmdArgs { public var format: [StringInterToken] { _format.isEmpty ? [ .interVar("app-pid"), .interVar("right-padding"), .literal(" | "), .interVar("app-bundle-id"), .interVar("right-padding"), .literal(" | "), .interVar("app-name"), ] : _format } } func parseListAppsCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ListAppsCmdArgs(rawArgs: args), args) .flatMap { if $0.json, let msg = getErrorIfFormatIsIncompatibleWithJson($0._format) { .failure(msg) } else { .cmd($0) } } } func getErrorIfFormatIsIncompatibleWithJson(_ format: [StringInterToken]) -> String? { for x in format { switch x { case .interVar("right-padding"): return "%{right-padding} interpolation variable is not allowed when --json is used" case .interVar: break // skip case .literal(let literal): if literal.contains(where: { $0 != " " }) { return "Only interpolation variables and spaces are allowed in '--format' when '--json' is used" } } } return nil } ================================================ FILE: Sources/Common/cmdArgs/impl/ListExecEnvVarsCmdArgs.swift ================================================ public struct ListExecEnvVarsCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .listExecEnvVars, allowInConfig: true, help: list_exec_env_vars_help_generated, flags: [:], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/ListModesCmdArgs.swift ================================================ public struct ListModesCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .listModes, allowInConfig: false, help: list_modes_help_generated, flags: [ "--count": trueBoolFlag(\.outputOnlyCount), "--current": trueBoolFlag(\.current), "--json": trueBoolFlag(\.json), ], posArgs: [], conflictingOptions: [ ["--count", "--current"], ["--count", "--json"], ], ) public var current: Bool = false public var json: Bool = false public var outputOnlyCount: Bool = false } func parseListModesCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ListModesCmdArgs(rawArgs: args), args) } ================================================ FILE: Sources/Common/cmdArgs/impl/ListMonitorsCmdArgs.swift ================================================ public struct ListMonitorsCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .listMonitors, allowInConfig: false, help: list_monitors_help_generated, flags: [ "--focused": boolFlag(\.focused), "--mouse": boolFlag(\.mouse), // Formatting flags "--format": formatParser(\._format, for: .monitor), "--count": trueBoolFlag(\.outputOnlyCount), "--json": trueBoolFlag(\.json), ], posArgs: [], conflictingOptions: [ ["--count", "--format"], ["--count", "--json"], ], ) public var focused: Bool? public var mouse: Bool? public var _format: [StringInterToken] = [] public var outputOnlyCount: Bool = false public var json: Bool = false } extension ListMonitorsCmdArgs { public var format: [StringInterToken] { _format.isEmpty ? [ .interVar("monitor-id"), .interVar("right-padding"), .literal(" | "), .interVar("monitor-name"), ] : _format } } func parseListMonitorsCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ListMonitorsCmdArgs(rawArgs: args), args) .flatMap { if $0.json, let msg = getErrorIfFormatIsIncompatibleWithJson($0._format) { .failure(msg) } else { .cmd($0) } } } ================================================ FILE: Sources/Common/cmdArgs/impl/ListWindowsCmdArgs.swift ================================================ import OrderedCollections private let workspace = "" private let workspaces = "\(workspace)..." public struct ListWindowsCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .listWindows, allowInConfig: false, help: list_windows_help_generated, flags: [ "--all": trueBoolFlag(\.allAlias), // Filtering flags "--focused": trueBoolFlag(\.filteringOptions.focused), "--monitor": ArgParser(\.filteringOptions.monitors, parseMonitorIds), "--workspace": ArgParser(\.filteringOptions.workspaces, parseWorkspaces), "--pid": singleValueSubArgParser(\.filteringOptions.pidFilter, "", Int32.init), "--app-bundle-id": singleValueSubArgParser(\.filteringOptions.appIdFilter, "") { $0 }, // Formatting flags "--format": formatParser(\._format, for: .window), "--count": trueBoolFlag(\.outputOnlyCount), "--json": trueBoolFlag(\.json), ], posArgs: [], conflictingOptions: [ ["--all", "--focused", "--workspace"], ["--all", "--focused", "--monitor"], ["--count", "--format"], ["--count", "--json"], ], ) fileprivate var allAlias: Bool = false public var filteringOptions = FilteringOptions() public var _format: [StringInterToken] = [] public var outputOnlyCount: Bool = false public var json: Bool = false public struct FilteringOptions: ConvenienceCopyable, Equatable, Sendable { public var monitors: [MonitorId] = [] public var focused: Bool = false public var workspaces: [WorkspaceFilter] = [] public var pidFilter: Int32? public var appIdFilter: String? } } extension ListWindowsCmdArgs { public var format: [StringInterToken] { _format.isEmpty ? [ .interVar("window-id"), .interVar("right-padding"), .literal(" | "), .interVar("app-name"), .interVar("right-padding"), .literal(" | "), .interVar("window-title"), ] : _format } } func parseListWindowsCmdArgs(_ args: StrArrSlice) -> ParsedCmd { let args = args.map { $0 == "--app-id" ? "--app-bundle-id" : $0 }.slice // Compatibility return parseSpecificCmdArgs(ListWindowsCmdArgs(commonState: .init(args)), args) .filter("Mandatory option is not specified (--focused|--all|--monitor|--workspace)") { raw in raw.filteringOptions.focused || raw.allAlias || !raw.filteringOptions.monitors.isEmpty || !raw.filteringOptions.workspaces.isEmpty } .filter("--all conflicts with \"filtering\" flags. Please use '--monitor all' instead of '--all' alias") { raw in raw.allAlias.implies(raw.filteringOptions == ListWindowsCmdArgs.FilteringOptions()) } .filter("--focused conflicts with other \"filtering\" flags") { raw in raw.filteringOptions.focused.implies(raw.filteringOptions.copy(\.focused, false) == ListWindowsCmdArgs.FilteringOptions()) } .map { raw in raw.allAlias ? raw.copy(\.filteringOptions.monitors, [.all]).copy(\.allAlias, false) : raw // Normalize alias } .flatMap { if $0.json, let msg = getErrorIfFormatIsIncompatibleWithJson($0._format) { .failure(msg) } else { .cmd($0) } } } func formatParser( _ keyPath: SendableWritableKeyPath, for kind: AeroObjKind, ) -> SubArgParser { return ArgParser(keyPath) { input in if let arg = input.nonFlagArgOrNil() { return switch arg.interpolationTokens(interpolationChar: "%") { case .success(let tokens): .succ(tokens, advanceBy: 1) case .failure(let err): .fail("Failed to parse . \(err)", advanceBy: 1) } } else { let values = getAvailableInterVars(for: kind).joined(separator: "\n").prependLines(" ") return .fail(" is mandatory. Possible values:\n\(values)", advanceBy: 0) } } } private func parseWorkspaces(input: SubArgParserInput) -> ParsedCliArgs<[WorkspaceFilter]> { let args = input.nonFlagArgs() let possibleValues = "\(workspace) possible values: (|focused|visible)" if args.isEmpty { return .fail("\(workspaces) is mandatory. \(possibleValues)", advanceBy: args.count) } var workspaces: [WorkspaceFilter] = [] var i = 0 for workspaceRaw in args { switch workspaceRaw { case "visible": workspaces.append(.visible) case "focused": workspaces.append(.focused) default: switch WorkspaceName.parse(workspaceRaw) { case .success(let unwrapped): workspaces.append(.name(unwrapped)) case .failure(let msg): return .fail(msg, advanceBy: i + 1) } } i += 1 } return .succ(workspaces, advanceBy: workspaces.count) } public enum WorkspaceFilter: Equatable, Sendable { case focused case visible case name(WorkspaceName) } public enum FormatVar: Equatable { case window(WindowFormatVar) case workspace(WorkspaceFormatVar) case app(AppFormatVar) case monitor(MonitorFormatVar) public enum WindowFormatVar: String, Equatable, CaseIterable { case windowId = "window-id" case windowIsFullscreen = "window-is-fullscreen" case windowTitle = "window-title" case windowLayout = "window-layout" // An alias for windowParentContainerLayout case windowParentContainerLayout = "window-parent-container-layout" } public enum WorkspaceFormatVar: String, Equatable, CaseIterable { case workspaceName = "workspace" case workspaceFocused = "workspace-is-focused" case workspaceVisible = "workspace-is-visible" case workspaceRootContainerLayout = "workspace-root-container-layout" } public enum AppFormatVar: String, Equatable, CaseIterable { case appBundleId = "app-bundle-id" case appName = "app-name" case appPid = "app-pid" case appExecPath = "app-exec-path" case appBundlePath = "app-bundle-path" } public enum MonitorFormatVar: String, Equatable, CaseIterable { case monitorId_oneBased = "monitor-id" case monitorAppKitNsScreenScreensId = "monitor-appkit-nsscreen-screens-id" case monitorName = "monitor-name" case monitorIsMain = "monitor-is-main" } } public enum PlainInterVar: String, CaseIterable { case rightPadding = "right-padding" case newline = "newline" case tab = "tab" } public enum AeroObjKind: CaseIterable, Sendable { case window, workspace, app, monitor } public func getAvailableInterVars(for kind: AeroObjKind) -> [String] { _getAvailableInterVars(for: kind) + PlainInterVar.allCases.map(\.rawValue) } private func _getAvailableInterVars(for kind: AeroObjKind) -> [String] { switch kind { case .app: FormatVar.AppFormatVar.allCases.map(\.rawValue) case .monitor: FormatVar.MonitorFormatVar.allCases.map(\.rawValue) case .workspace: FormatVar.WorkspaceFormatVar.allCases.map(\.rawValue) + _getAvailableInterVars(for: .monitor) case .window: FormatVar.WindowFormatVar.allCases.map(\.rawValue) + _getAvailableInterVars(for: .workspace) + _getAvailableInterVars(for: .app) } } ================================================ FILE: Sources/Common/cmdArgs/impl/ListWorkspacesCmdArgs.swift ================================================ import OrderedCollections let onitor = "" let _monitors = "\(onitor)..." public struct ListWorkspacesCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .listWorkspaces, allowInConfig: false, help: list_workspaces_help_generated, flags: [ // Aliases "--focused": trueBoolFlag(\.focused), "--all": trueBoolFlag(\.all), // Filtering flags "--visible": boolFlag(\.filteringOptions.visible), "--empty": boolFlag(\.filteringOptions.empty), "--monitor": ArgParser(\.filteringOptions.onMonitors, parseMonitorIds), // Formatting flags "--format": formatParser(\._format, for: .workspace), "--count": trueBoolFlag(\.outputOnlyCount), "--json": trueBoolFlag(\.json), ], posArgs: [], conflictingOptions: [ ["--all", "--focused", "--monitor"], ["--count", "--format"], ["--count", "--json"], ], ) fileprivate var all: Bool = false // Alias fileprivate var focused: Bool = false // Alias public var filteringOptions = FilteringOptions() public var _format: [StringInterToken] = [.interVar("workspace")] public var outputOnlyCount: Bool = false public var json: Bool = false public struct FilteringOptions: ConvenienceCopyable, Equatable, Sendable { public var onMonitors: [MonitorId] = [] public var visible: Bool? public var empty: Bool? } } extension ListWorkspacesCmdArgs { public var format: [StringInterToken] { _format.isEmpty ? [.interVar("workspace")] : _format } } func parseListWorkspacesCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ListWorkspacesCmdArgs(commonState: .init(args)), args) .filter("Mandatory option is not specified (--all|--focused|--monitor)") { raw in raw.all || raw.focused || !raw.filteringOptions.onMonitors.isEmpty } .filter("--all conflicts with any other \"filtering\" options") { raw in raw.all.implies(raw.filteringOptions == ListWorkspacesCmdArgs.FilteringOptions()) } .filter("--focused conflicts with all other \"filtering\" options") { raw in raw.focused.implies(raw.filteringOptions == ListWorkspacesCmdArgs.FilteringOptions()) } .map { raw in raw.all ? raw.copy(\.filteringOptions.onMonitors, [.all]).copy(\.all, false) : raw } .map { raw in // Expand alias raw.focused ? raw.copy(\.filteringOptions.onMonitors, [.focused]) .copy(\.filteringOptions.visible, true) .copy(\.focused, false) : raw } .flatMap { if $0.json, let msg = getErrorIfFormatIsIncompatibleWithJson($0._format) { .failure(msg) } else { .cmd($0) } } } func parseMonitorIds(input: SubArgParserInput) -> ParsedCliArgs<[MonitorId]> { let args = input.nonFlagArgs() let possibleValues = "\(onitor) possible values: (|focused|mouse|all)" if args.isEmpty { return .fail("\(_monitors) is mandatory. \(possibleValues)", advanceBy: args.count) } var monitors: [MonitorId] = [] var i = 0 for monitor in args { switch Int.init(monitor) { case .some(let unwrapped): monitors.append(.index(unwrapped - 1)) case _ where monitor == "mouse": monitors.append(.mouse) case _ where monitor == "all": monitors.append(.all) case _ where monitor == "focused": monitors.append(.focused) default: return .fail("Can't parse monitor ID '\(monitor)'. \(possibleValues)", advanceBy: i + 1) } i += 1 } return .succ(monitors, advanceBy: monitors.count) } public enum MonitorId: Equatable, Sendable { case focused case all case mouse case index(Int) } ================================================ FILE: Sources/Common/cmdArgs/impl/MacosNativeFullscreenCmdArgs.swift ================================================ public struct MacosNativeFullscreenCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .macosNativeFullscreen, allowInConfig: true, help: macos_native_fullscreen_help_generated, flags: [ "--fail-if-noop": trueBoolFlag(\.failIfNoop), "--window-id": optionalWindowIdFlag(), ], posArgs: [ArgParser(\.toggle, parseToggleEnum)], ) public var toggle: ToggleEnum = .toggle public var failIfNoop: Bool = false } func parseMacosNativeFullscreenCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MacosNativeFullscreenCmdArgs(rawArgs: args), args) .filter("--fail-if-noop requires 'on' or 'off' argument") { $0.failIfNoop.implies($0.toggle == .on || $0.toggle == .off) } } public enum ToggleEnum: Sendable { case on, off, toggle } func parseToggleEnum(i: PosArgParserInput) -> ParsedCliArgs { switch i.arg { case "on": .succ(.on, advanceBy: 1) case "off": .succ(.off, advanceBy: 1) default: .fail("Can't parse '\(i.arg)'. Possible values: on|off", advanceBy: 1) } } ================================================ FILE: Sources/Common/cmdArgs/impl/MacosNativeMinimizeCmdArgs.swift ================================================ public struct MacosNativeMinimizeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .macosNativeMinimize, allowInConfig: true, help: macos_native_minimize_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), ], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/ModeCmdArgs.swift ================================================ public struct ModeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .mode, allowInConfig: true, help: mode_help_generated, flags: [:], posArgs: [newMandatoryPosArgParser(\.targetMode, consumeStrCliArg, placeholder: "")], ) public var targetMode: Lateinit = .uninitialized } func consumeStrCliArg(i: PosArgParserInput) -> ParsedCliArgs { .succ(i.arg, advanceBy: 1) } ================================================ FILE: Sources/Common/cmdArgs/impl/MoveCmdArgs.swift ================================================ public struct MoveCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .move, allowInConfig: true, help: move_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), "--boundaries": ArgParser(\.rawBoundaries, upcastArgParserFun(parseBoundaries)), "--boundaries-action": ArgParser(\.rawBoundariesAction, upcastArgParserFun(parseBoundariesAction)), ], posArgs: [newMandatoryPosArgParser(\.direction, parseCardinalDirectionArg, placeholder: CardinalDirection.unionLiteral)], ) public var direction: Lateinit = .uninitialized public var rawBoundaries: Boundaries? = nil public var rawBoundariesAction: WhenBoundariesCrossed? = nil public init(rawArgs: [String], _ direction: CardinalDirection) { self.commonState = .init(rawArgs.slice) self.direction = .initialized(direction) } public enum Boundaries: String, CaseIterable, Equatable, Sendable { case workspace case allMonitorsOuterFrame = "all-monitors-outer-frame" } public enum WhenBoundariesCrossed: String, CaseIterable, Equatable, Sendable { case stop = "stop" case fail = "fail" case createImplicitContainer = "create-implicit-container" } } extension MoveCmdArgs { public var boundaries: Boundaries { rawBoundaries ?? .workspace } public var boundariesAction: WhenBoundariesCrossed { rawBoundariesAction ?? .createImplicitContainer } } func parseMoveCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MoveCmdArgs(rawArgs: args), args) } private func parseBoundaries(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { return .init(parseEnum(arg, MoveCmdArgs.Boundaries.self), advanceBy: 1) } else { return .fail(" is mandatory", advanceBy: 0) } } private func parseBoundariesAction(i: SubArgParserInput) -> ParsedCliArgs { if let arg = i.nonFlagArgOrNil() { return .init(parseEnum(arg, MoveCmdArgs.WhenBoundariesCrossed.self), advanceBy: 1) } else { return .fail(" is mandatory", advanceBy: 0) } } ================================================ FILE: Sources/Common/cmdArgs/impl/MoveMouseCmdArgs.swift ================================================ public struct MoveMouseCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .moveMouse, allowInConfig: true, help: move_mouse_help_generated, flags: [ "--fail-if-noop": trueBoolFlag(\.failIfNoop), ], posArgs: [newMandatoryPosArgParser(\.mouseTarget, parseMouseTarget, placeholder: "")], ) public var failIfNoop: Bool = false public var mouseTarget: Lateinit = .uninitialized } func parseMouseTarget(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, MouseTarget.self), advanceBy: 1) } func parseMoveMouseCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MoveMouseCmdArgs(rawArgs: args), args) .filter("--fail-if-noop is only compatible with window-lazy-center or monitor-lazy-center") { $0.failIfNoop.implies($0.mouseTarget.val == .windowLazyCenter || $0.mouseTarget.val == .monitorLazyCenter) } } public enum MouseTarget: String, CaseIterable, Sendable { case monitorLazyCenter = "monitor-lazy-center" case monitorForceCenter = "monitor-force-center" case windowLazyCenter = "window-lazy-center" case windowForceCenter = "window-force-center" } ================================================ FILE: Sources/Common/cmdArgs/impl/MoveNodeToMonitorCmdArgs.swift ================================================ public struct MoveNodeToMonitorCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .moveNodeToMonitor, allowInConfig: true, help: move_node_to_monitor_help_generated, flags: [ // "Own" option "--wrap-around": trueBoolFlag(\.wrapAround), "--window-id": optionalWindowIdFlag(), "--focus-follows-window": trueBoolFlag(\.focusFollowsWindow), "--fail-if-noop": trueBoolFlag(\.failIfNoop), ], posArgs: [newMandatoryPosArgParser(\.target, parseTarget, placeholder: MonitorTarget.cases.joinedCliArgs)], ) public init(target: MonitorTarget) { self.commonState = .init([]) self.target = .initialized(target) } public var failIfNoop: Bool = false public var focusFollowsWindow: Bool = false public var wrapAround: Bool = false public var target: Lateinit = .uninitialized } func parseMoveNodeToMonitorCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MoveNodeToMonitorCmdArgs(rawArgs: args), args) .filter("--wrap-around is incompatible with argument") { $0.wrapAround.implies(!$0.target.val.isPatterns) } .filter("--fail-if-noop is incompatible with \(MonitorTarget.casesExceptPatterns.joinedCliArgs)") { $0.failIfNoop.implies($0.target.val.isPatterns) } } ================================================ FILE: Sources/Common/cmdArgs/impl/MoveNodeToWorkspaceCmdArgs.swift ================================================ public struct MoveNodeToWorkspaceCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .moveNodeToWorkspace, allowInConfig: true, help: move_node_to_workspace_help_generated, flags: [ "--wrap-around": optionalTrueBoolFlag(\._wrapAround), "--fail-if-noop": trueBoolFlag(\.failIfNoop), "--window-id": optionalWindowIdFlag(), "--focus-follows-window": trueBoolFlag(\.focusFollowsWindow), "--stdin": optionalTrueBoolFlag(\.explicitStdinFlag), "--no-stdin": optionalFalseBoolFlag(\.explicitStdinFlag), ], posArgs: [newMandatoryPosArgParser(\.target, parseWorkspaceTarget, placeholder: workspaceTargetPlaceholder)], conflictingOptions: [ ["--stdin", "--no-stdin"], ], ) public var _wrapAround: Bool? public var explicitStdinFlag: Bool? = nil public var failIfNoop: Bool = false public var focusFollowsWindow: Bool = false public var target: Lateinit = .uninitialized public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } } extension MoveNodeToWorkspaceCmdArgs { public var wrapAround: Bool { _wrapAround ?? false } public var useStdin: Bool { explicitStdinFlag ?? false } } func parseMoveNodeToWorkspaceCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MoveNodeToWorkspaceCmdArgs(rawArgs: args), args) .filter("--wrapAround requires using (prev|next) argument") { ($0._wrapAround != nil).implies($0.target.val.isRelatve) } .filterNot("--fail-if-noop is incompatible with (next|prev)") { $0.failIfNoop && $0.target.val.isRelatve } .filterNot("--window-id is incompatible with (next|prev)") { $0.windowId != nil && $0.target.val.isRelatve } .filter("--stdin and --no-stdin require using \(NextPrev.unionLiteral) argument") { ($0.explicitStdinFlag != nil).implies($0.target.val.isRelatve) } } ================================================ FILE: Sources/Common/cmdArgs/impl/MoveWorkpsaceToMonitorCmdArgs.swift ================================================ public struct MoveWorkspaceToMonitorCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .moveWorkspaceToMonitor, allowInConfig: true, help: move_workspace_to_monitor_help_generated, flags: [ "--wrap-around": trueBoolFlag(\.wrapAround), "--workspace": optionalWorkspaceFlag(), ], posArgs: [ newMandatoryPosArgParser(\.target, parseTarget, placeholder: MonitorTarget.cases.joinedCliArgs), ], ) public var wrapAround: Bool = false public var target: Lateinit = .uninitialized } func parseWorkspaceToMonitorCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(MoveWorkspaceToMonitorCmdArgs(rawArgs: args), args) .filter("--wrap-around is incompatible with argument") { $0.wrapAround.implies(!$0.target.val.isPatterns) } } ================================================ FILE: Sources/Common/cmdArgs/impl/ReloadConfigCmdArgs.swift ================================================ public struct ReloadConfigCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .reloadConfig, allowInConfig: true, help: reload_config_help_generated, flags: [ "--no-gui": trueBoolFlag(\.noGui), "--dry-run": trueBoolFlag(\.dryRun), ], posArgs: [], ) public var noGui: Bool = false public var dryRun: Bool = false } ================================================ FILE: Sources/Common/cmdArgs/impl/ResizeCmdArgs.swift ================================================ public struct ResizeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .resize, allowInConfig: true, help: resize_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), ], posArgs: [ newMandatoryPosArgParser(\.dimension, parseDimension, placeholder: "(smart|smart-opposite|width|height)"), newMandatoryPosArgParser(\.units, parseUnits, placeholder: "[+|-]"), ], ) public var dimension: Lateinit = .uninitialized public var units: Lateinit = .uninitialized public init( rawArgs: [String], dimension: Dimension, units: Units, ) { self.commonState = .init(rawArgs.slice) self.dimension = .initialized(dimension) self.units = .initialized(units) } public enum Dimension: String, CaseIterable, Equatable, Sendable { case width, height, smart case smartOpposite = "smart-opposite" } public enum Units: Equatable, Sendable { case set(UInt) case add(UInt) case subtract(UInt) } } func parseResizeCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(ResizeCmdArgs(rawArgs: args), args) } private func parseDimension(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, ResizeCmdArgs.Dimension.self), advanceBy: 1) } private func parseUnits(i: PosArgParserInput) -> ParsedCliArgs { if let number = UInt(i.arg.removePrefix("+").removePrefix("-")) { switch true { case i.arg.starts(with: "+"): .succ(.add(number), advanceBy: 1) case i.arg.starts(with: "-"): .succ(.subtract(number), advanceBy: 1) default: .succ(.set(number), advanceBy: 1) } } else { .fail(" argument must be a number", advanceBy: 1) } } ================================================ FILE: Sources/Common/cmdArgs/impl/SplitCmdArgs.swift ================================================ public struct SplitCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .split, allowInConfig: true, help: split_help_generated, flags: [ "--window-id": optionalWindowIdFlag(), ], posArgs: [newMandatoryPosArgParser(\.arg, parseSplitArg, placeholder: SplitArg.unionLiteral)], ) public var arg: Lateinit = .uninitialized public init(rawArgs: [String], _ arg: SplitArg) { self.commonState = .init(rawArgs.slice) self.arg = .initialized(arg) } public enum SplitArg: String, CaseIterable, Sendable { case horizontal, vertical, opposite } } func parseSplitCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(SplitCmdArgs(rawArgs: args), args) } private func parseSplitArg(i: PosArgParserInput) -> ParsedCliArgs { .init(parseEnum(i.arg, SplitCmdArgs.SplitArg.self), advanceBy: 1) } ================================================ FILE: Sources/Common/cmdArgs/impl/SubscribeCmdArgs.swift ================================================ public struct SubscribeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState fileprivate init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .subscribe, allowInConfig: false, help: subscribe_help_generated, flags: [ "--all": trueBoolFlag(\.allAlias), "--no-send-initial": falseBoolFlag(\.sendInitial), ], posArgs: [ArgParser(\.events, parseEventTypes)], ) fileprivate var allAlias: Bool = false public var sendInitial = true public var events: Set = [] } public func parseSubscribeCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(SubscribeCmdArgs(rawArgs: args), args) .filter("Either --all or at least one must be specified") { raw in raw.allAlias || !raw.events.isEmpty } .filter("--all conflicts with specifying individual events") { raw in raw.allAlias.implies(raw.events.isEmpty) } .map { raw in raw.allAlias ? raw.copy(\.events, Set(ServerEventType.allCases)).copy(\.allAlias, false) : raw } } private func parseEventTypes(_ input: ArgParserInput) -> ParsedCliArgs> { let args = input.nonFlagArgs() var events: Set = [] var errorMsg: String? = nil for arg in args { switch parseEnum(arg, ServerEventType.self) { case .success(let event): if events.contains(event) { errorMsg = "Duplicate event '\(arg)'" } events.insert(event) case .failure(let errorMsg): return .fail(errorMsg, advanceBy: events.count + 1) } } if let errorMsg { return .fail(errorMsg, advanceBy: args.count) } else { return .succ(events, advanceBy: args.count) } } public enum ServerEventType: String, Codable, CaseIterable, Sendable { case focusChanged = "focus-changed" case focusedMonitorChanged = "focused-monitor-changed" case workspaceChanged = "focused-workspace-changed" case modeChanged = "mode-changed" case windowDetected = "window-detected" case bindingTriggered = "binding-triggered" } ================================================ FILE: Sources/Common/cmdArgs/impl/SummonWorkspaceCmdArgs.swift ================================================ public struct SummonWorkspaceCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .summonWorkspace, allowInConfig: true, help: summon_workspace_help_generated, flags: [ "--fail-if-noop": trueBoolFlag(\.failIfNoop), ], posArgs: [newMandatoryPosArgParser(\.target, parseWorkspaceName, placeholder: "")], ) public var target: Lateinit = .uninitialized public var failIfNoop: Bool = false } private func parseWorkspaceName(i: PosArgParserInput) -> ParsedCliArgs { .init(WorkspaceName.parse(i.arg), advanceBy: 1) } ================================================ FILE: Sources/Common/cmdArgs/impl/SwapCmdArgs.swift ================================================ public struct SwapCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .swap, allowInConfig: true, help: swap_help_generated, flags: [ "--swap-focus": trueBoolFlag(\.swapFocus), "--wrap-around": trueBoolFlag(\.wrapAround), "--window-id": optionalWindowIdFlag(), ], posArgs: [newMandatoryPosArgParser(\.target, parseCardinalOrDfsDirection, placeholder: CardinalOrDfsDirection.unionLiteral)], ) public var target: Lateinit = .uninitialized public var swapFocus: Bool = false public var wrapAround: Bool = false public init(rawArgs: [String], target: CardinalOrDfsDirection) { self.commonState = .init(rawArgs.slice) self.target = .initialized(target) } } func parseSwapCmdArgs(_ args: StrArrSlice) -> ParsedCmd { return parseSpecificCmdArgs(SwapCmdArgs(rawArgs: args), args) } ================================================ FILE: Sources/Common/cmdArgs/impl/TriggerBindingCmdArgs.swift ================================================ public struct TriggerBindingCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public static let parser: CmdParser = .init( kind: .triggerBinding, allowInConfig: true, help: trigger_binding_help_generated, flags: [ "--mode": singleValueSubArgParser(\._mode, "") { $0 }, ], posArgs: [newMandatoryPosArgParser(\.binding, consumeStrCliArg, placeholder: "")], ) public var _mode: String? = nil public var binding: Lateinit = .uninitialized } extension TriggerBindingCmdArgs { public var mode: String { _mode.orDie() } } func parseTriggerBindingCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(TriggerBindingCmdArgs(commonState: .init(args)), args) .filter("--mode flag is mandatory") { $0._mode != nil } } ================================================ FILE: Sources/Common/cmdArgs/impl/VolumeCmdArgs.swift ================================================ public struct VolumeCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .volume, allowInConfig: true, help: volume_help_generated, flags: [ "--no-gui": falseBoolFlag(\.gui), ], posArgs: [newMandatoryPosArgParser(\.action, parseVolumeAction, placeholder: VolumeAction.argsUnion)], ) public var gui: Bool = true public var action: Lateinit = .uninitialized } public enum VolumeAction: Equatable, Sendable { case up, down, muteToggle, muteOn, muteOff case set(Int) static let argsUnion: String = "(up|down|mute-toggle|mute-on|mute-off|set)" } func parseVolumeAction(i: PosArgParserInput) -> ParsedCliArgs { switch i.arg { case "up": return .succ(.up, advanceBy: 1) case "down": return .succ(.down, advanceBy: 1) case "mute-toggle": return .succ(.muteToggle, advanceBy: 1) case "mute-off": return .succ(.muteOff, advanceBy: 1) case "mute-on": return .succ(.muteOn, advanceBy: 1) case "set": guard let arg = i.getOrNil(relativeIndex: 1) else { return .fail("set argument must be followed by ", advanceBy: 1) } guard let int = Int(arg) else { return .fail("Can't parse number '\(arg)'", advanceBy: 2) } if !(0 ... 100).contains(int) { return .fail("\(int) must be in range from 0 to 100", advanceBy: 2) } return .succ(.set(int), advanceBy: 2) default: return .fail("Unknown argument '\(i.arg)'. Possible values: \(VolumeAction.argsUnion)", advanceBy: 1) } } ================================================ FILE: Sources/Common/cmdArgs/impl/WorkspaceBackAndForthCmdArgs.swift ================================================ public struct WorkspaceBackAndForthCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .workspaceBackAndForth, allowInConfig: true, help: workspace_back_and_forth_help_generated, flags: [:], posArgs: [], ) } ================================================ FILE: Sources/Common/cmdArgs/impl/WorkspaceCmdArgs.swift ================================================ public struct WorkspaceCmdArgs: CmdArgs { /*conforms*/ public var commonState: CmdArgsCommonState public init(rawArgs: StrArrSlice) { self.commonState = .init(rawArgs) } public static let parser: CmdParser = .init( kind: .workspace, allowInConfig: true, help: workspace_help_generated, flags: [ "--auto-back-and-forth": optionalTrueBoolFlag(\._autoBackAndForth), "--wrap-around": optionalTrueBoolFlag(\._wrapAround), "--fail-if-noop": trueBoolFlag(\.failIfNoop), "--stdin": optionalTrueBoolFlag(\.explicitStdinFlag), "--no-stdin": optionalFalseBoolFlag(\.explicitStdinFlag), ], posArgs: [newMandatoryPosArgParser(\.target, parseWorkspaceTarget, placeholder: workspaceTargetPlaceholder)], conflictingOptions: [ ["--stdin", "--no-stdin"], ], ) public var target: Lateinit = .uninitialized public var _autoBackAndForth: Bool? public var failIfNoop: Bool = false public var _wrapAround: Bool? public var explicitStdinFlag: Bool? = nil } func parseWorkspaceCmdArgs(_ args: StrArrSlice) -> ParsedCmd { parseSpecificCmdArgs(WorkspaceCmdArgs(rawArgs: args), args) .filter("--wrapAround requires using \(NextPrev.unionLiteral) argument") { ($0._wrapAround != nil).implies($0.target.val.isRelatve) } .filterNot("--auto-back-and-forth is incompatible with \(NextPrev.unionLiteral)") { $0._autoBackAndForth != nil && $0.target.val.isRelatve } .filterNot("--fail-if-noop is incompatible with \(NextPrev.unionLiteral)") { $0.failIfNoop && $0.target.val.isRelatve } .filterNot("--fail-if-noop is incompatible with --auto-back-and-forth") { $0.autoBackAndForth && $0.failIfNoop } .filter("--stdin and --no-stdin require using \(NextPrev.unionLiteral) argument") { ($0.explicitStdinFlag != nil).implies($0.target.val.isRelatve) } } extension WorkspaceCmdArgs { public var wrapAround: Bool { _wrapAround ?? false } public var autoBackAndForth: Bool { _autoBackAndForth ?? false } public var useStdin: Bool { explicitStdinFlag ?? false } } public enum WorkspaceTarget: Equatable, Sendable { case relative(NextPrev) case direct(WorkspaceName) public var isRelatve: Bool { switch self { case .relative: true default: false } } public func workspaceNameOrNil() -> WorkspaceName? { switch self { case .direct(let name): name case .relative: nil } } } let workspaceTargetPlaceholder = "(|next|prev)" func parseWorkspaceTarget(i: PosArgParserInput) -> ParsedCliArgs { switch i.arg { case "next": .succ(.relative(.next), advanceBy: 1) case "prev": .succ(.relative(.prev), advanceBy: 1) default: .init(WorkspaceName.parse(i.arg).map(WorkspaceTarget.direct), advanceBy: 1) } } ================================================ FILE: Sources/Common/cmdArgs/parseCmdArgs.swift ================================================ public func parseCmdArgs(_ args: StrArrSlice) -> ParsedCmd { let subcommand = String(args.first ?? "") if subcommand.isEmpty { return .failure("Can't parse empty string command") } if let subcommandParser: any SubCommandParserProtocol = subcommandParsers[subcommand] { return subcommandParser.parse(args: args.slice(1...).orDie()) } else { return .failure("Unrecognized subcommand '\(subcommand)'") } } public protocol CmdArgs: ConvenienceCopyable, Equatable, CustomStringConvertible, AeroAny, Sendable { static var parser: CmdParser { get } var commonState: CmdArgsCommonState { get set } } public struct CmdArgsCommonState: ConvenienceCopyable, Equatable, Sendable { let rawArgsForStrRepr: EquatableNoop var windowId: UInt32? = nil var workspaceName: WorkspaceName? = nil public init(_ raw: StrArrSlice) { rawArgsForStrRepr = .init(raw) } } extension CmdArgs { public static var info: CmdStaticInfo { Self.parser.info } public var windowId: UInt32? { get { commonState.windowId } set(value) { commonState.windowId = value } } public var workspaceName: WorkspaceName? { get { commonState.workspaceName } set(value) { commonState.workspaceName = value } } public func equals(_ other: any CmdArgs) -> Bool { // My brain is cursed with Java (other as? Self).flatMap { self == $0 } ?? false } public var description: String { switch Self.info.kind { case .execAndForget: CmdKind.execAndForget.rawValue + " " + (self as! ExecAndForgetCmdArgs).bashScript default: ([Self.info.kind.rawValue] + commonState.rawArgsForStrRepr.value.toArray()).joinArgs() } } } public struct CmdParser: Sendable { let info: CmdStaticInfo let flags: [String: any ArgParserProtocol] let positionalArgs: [any ArgParserProtocol] let conflictingOptions: [Set] init( kind: CmdKind, allowInConfig: Bool, help: String, flags: [String: any ArgParserProtocol], posArgs: [any ArgParserProtocol], conflictingOptions: [Set] = [], ) { self.info = CmdStaticInfo(help: help, kind: kind, allowInConfig: allowInConfig) self.flags = flags self.positionalArgs = posArgs self.conflictingOptions = conflictingOptions } } public struct CmdStaticInfo: Equatable, Sendable { public let help: String public let kind: CmdKind public let allowInConfig: Bool // Query commands are prohibited in config public init( help: String, kind: CmdKind, allowInConfig: Bool, ) { self.help = help self.kind = kind self.allowInConfig = allowInConfig } } ================================================ FILE: Sources/Common/cmdArgs/parseSpecificCmdArgs.swift ================================================ func parseSpecificCmdArgs(_ raw: T, _ args: StrArrSlice) -> ParsedCmd { var raw = raw var errors: [String] = [] var posArgumentParserIndex = 0 var options: Set = Set() var index = 0 while index < args.count { let arg = args[index] if arg == "-h" || arg == "--help" { return .help(T.info.help) } else if arg.starts(with: "-") && !isResizeNegativeUnitsArg(raw, arg: arg) { if let optionParser = T.parser.flags[arg] { index += 1 if !options.insert(arg).inserted { errors.append("Duplicated option \(arg.singleQuoted)") } raw = optionParser.transformRaw(raw, &index, SubArgParserInput(superArg: arg, index: index, args: args), &errors) } else { errors.append("Unknown flag \(arg.singleQuoted)") break } } else if let parser = T.parser.positionalArgs.getOrNil(atIndex: posArgumentParserIndex) { raw = parser.transformRaw(raw, &index, PosArgParserInput(index: index, args: args), &errors) posArgumentParserIndex += 1 } else { errors.append("Unknown argument \(arg.singleQuoted)") break } } for arg in T.parser.positionalArgs[posArgumentParserIndex...] { if let placeholder = arg.context.argPlaceholderIfMandatory { errors.append("Argument \(placeholder.singleQuoted) is mandatory") } } for conflictSet in T.parser.conflictingOptions { let mutualOptions = conflictSet.intersection(options) if mutualOptions.count > 1 { errors.append("Conflicting options: \(mutualOptions.sorted().joined(separator: ", "))") break } } return errors.isEmpty ? .cmd(raw) : .failure(errors.joinErrors()) } public enum ParsedCmd: Sendable { case cmd(T) case help(String) case failure(String) public func map(_ mapper: (T) -> R) -> ParsedCmd { flatMap { .cmd(mapper($0)) } } public func filter(_ msg: @autoclosure () -> String, _ predicate: (T) -> Bool) -> ParsedCmd { flatMap { this in predicate(this) ? .cmd(this) : .failure(msg()) } } public func filterNot(_ msg: @autoclosure () -> String, _ predicate: (T) -> Bool) -> ParsedCmd { flatMap { this in !predicate(this) ? .cmd(this) : .failure(msg()) } } public func flatMap(_ mapper: (T) -> ParsedCmd) -> ParsedCmd { return switch self { case .cmd(let cmd): mapper(cmd) case .help(let help): .help(help) case .failure(let fail): .failure(fail) } } public var cmdOrNil: T? { switch self { case .cmd(let t): t default: nil } } public func unwrap() -> (T?, String?, String?) { switch self { case .cmd(let command): (command, nil, nil) case .help(let help): (nil, help, nil) case .failure(let error): (nil, nil, error) } } } extension ArgParserProtocol where Root: ConvenienceCopyable { fileprivate func transformRaw(_ raw: consuming Root, _ index: inout Int, _ input: Input, _ errors: inout [String]) -> Root { let parsedCliArgs = parse(input) index += parsedCliArgs.advanceBy if let value = parsedCliArgs.value.getOrNil(appendErrorTo: &errors) { return raw.copy(keyPath, value) } else { return raw } } } // Hack to preserve backwards compatibility private func isResizeNegativeUnitsArg(_ raw: any CmdArgs, arg: String) -> Bool { var iter = arg.makeIterator() return raw is ResizeCmdArgs && iter.next() == "-" && iter.next()?.isNumber == true } ================================================ FILE: Sources/Common/cmdArgs/splitArgs.swift ================================================ extension String { // Input: " foo bar ". Output: ["foo", "bar"] // Input "foo 'bar baz'". Output ["foo", "bar baz"] public func splitArgs() -> Parsed<[String]> { var result: [String] = [] var arg: String = "" var state: State = .parseArgWhitespaceSeparator for char in self { switch state { // State machine case .parseArgWhitespaceSeparator: if char == "\"" || char == "\'" { state = .parseArg(quoteChar: char) } else if !char.isWhitespace { state = .parseArg(quoteChar: nil) arg.append(char) } case .parseArg(let quoteChar): if quoteChar == char { result.append(arg) arg = "" state = .parseArgWhitespaceSeparator } else if quoteChar == nil && char.isWhitespace { result.append(arg) state = .parseArgWhitespaceSeparator arg = "" } else if quoteChar == nil && char.isQuote { return .failure("Unexpected quote \(char) in argument '\(arg)'") } else { arg.append(char) } } } if case .parseArg(let quoteChar) = state { if let quoteChar { return .failure("Last quote \(quoteChar) isn't closed") } else { result.append(arg) } } return .success(result) } } extension Character { fileprivate var isQuote: Bool { self == "\'" || self == "\"" } } private enum State { case parseArg(quoteChar: Character?) case parseArgWhitespaceSeparator } extension [String] { public func joinArgs() -> String { self.map { let containsWhitespaces = $0.rangeOfCharacter(from: .whitespacesAndNewlines) != nil let containsSingleQuote = $0.contains("'") let containsDoubleQuote = $0.contains("\"") return switch true { case containsDoubleQuote && !containsSingleQuote: $0.quoted(with: "'") case containsSingleQuote && !containsDoubleQuote: $0.quoted(with: "\"") case containsSingleQuote && containsDoubleQuote: // Technically shouldn't be possible according to splitArgs $0.replacing("'", with: "\\'").replacing("\"", with: "\\\"").quoted(with: "\"") case containsWhitespaces: $0.quoted(with: "'") default: $0 } }.joined(separator: " ") } } ================================================ FILE: Sources/Common/cmdArgs/subcommandParsers.swift ================================================ let subcommandParsers: [String: any SubCommandParserProtocol] = initSubcommands() protocol SubCommandParserProtocol: Sendable { associatedtype T where T: CmdArgs var _parse: @Sendable (StrArrSlice) -> ParsedCmd { get } } extension SubCommandParserProtocol { func parse(args: StrArrSlice) -> ParsedCmd { _parse(args).map { $0 } } } struct SubCommandParser: SubCommandParserProtocol, Sendable { let _parse: @Sendable (StrArrSlice) -> ParsedCmd init(_ parser: @escaping @Sendable (StrArrSlice) -> ParsedCmd) { _parse = parser } init(_ raw: @escaping @Sendable (StrArrSlice) -> T) { _parse = { args in parseSpecificCmdArgs(raw(args), args) } } } ================================================ FILE: Sources/Common/cmdHelpGenerated.swift ================================================ // FILE IS GENERATED FROM docs/aerospace-*.adoc files // TO REGENERATE THE FILE RUN generate.sh let balance_sizes_help_generated = """ USAGE: balance-sizes [-h|--help] [--workspace ] """ let close_all_windows_but_current_help_generated = """ USAGE: close-all-windows-but-current [-h|--help] [--quit-if-last-window] """ let close_help_generated = """ USAGE: close [-h|--help] [--quit-if-last-window] [--window-id ] """ let config_help_generated = """ USAGE: config [-h|--help] --get [--json] [--keys] OR: config [-h|--help] --major-keys OR: config [-h|--help] --all-keys OR: config [-h|--help] --config-path """ let debug_windows_help_generated = """ USAGE: debug-windows [-h|--help] [--window-id ] """ let enable_help_generated = """ USAGE: enable [-h|--help] toggle OR: enable [-h|--help] on [--fail-if-noop] OR: enable [-h|--help] off [--fail-if-noop] """ let exec_and_forget_help_generated = """ USAGE: exec-and-forget """ let flatten_workspace_tree_help_generated = """ USAGE: flatten-workspace-tree [-h|--help] [--workspace ] """ let focus_back_and_forth_help_generated = """ USAGE: focus-back-and-forth [-h|--help] """ let focus_monitor_help_generated = """ USAGE: focus-monitor [-h|--help] [--wrap-around] (left|down|up|right) OR: focus-monitor [-h|--help] [--wrap-around] (next|prev) OR: focus-monitor [-h|--help] ... """ let focus_help_generated = """ USAGE: focus [-h|--help] [--ignore-floating] [--wrap-around] [--boundaries ] [--boundaries-action ] (left|down|up|right) OR: focus [-h|--help] [--ignore-floating] [--wrap-around] [--boundaries ] [--boundaries-action ] (dfs-next|dfs-prev) OR: focus [-h|--help] --window-id OR: focus [-h|--help] --dfs-index """ let fullscreen_help_generated = """ USAGE: fullscreen [-h|--help] [--window-id ] [--no-outer-gaps] OR: fullscreen [-h|--help] on [--window-id ] [--no-outer-gaps] [--fail-if-noop] OR: fullscreen [-h|--help] off [--window-id ] [--fail-if-noop] """ let join_with_help_generated = """ USAGE: join-with [-h|--help] [--window-id ] (left|down|up|right) """ let layout_help_generated = """ USAGE: layout [-h|--help] [--window-id ] (h_tiles|v_tiles|h_accordion|v_accordion|tiles|accordion|horizontal|vertical|tiling|floating)... """ let list_apps_help_generated = """ USAGE: list-apps [-h|--help] [--macos-native-hidden [no]] [--format ] [--count] [--json] """ let list_exec_env_vars_help_generated = """ USAGE: list-exec-env-vars [-h|--help] """ let list_modes_help_generated = """ USAGE: list-modes [-h|--help] [--current] [--count] [--json] """ let list_monitors_help_generated = """ USAGE: list-monitors [-h|--help] [--focused [no]] [--mouse [no]] [--format ] [--count] [--json] """ let list_windows_help_generated = """ USAGE: list-windows [-h|--help] (--workspace ...|--monitor ...) [--monitor ...] [--workspace ...] [--pid ] [--app-bundle-id ] [--format ] [--count] [--json] OR: list-windows [-h|--help] --all [--format ] [--count] [--json] OR: list-windows [-h|--help] --focused [--format ] [--count] [--json] """ let list_workspaces_help_generated = """ USAGE: list-workspaces [-h|--help] --monitor ... [--visible [no]] [--empty [no]] [--format ] [--count] [--json] OR: list-workspaces [-h|--help] --all [--format ] [--count] [--json] OR: list-workspaces [-h|--help] --focused [--format ] [--count] [--json] """ let macos_native_fullscreen_help_generated = """ USAGE: macos-native-fullscreen [-h|--help] [--window-id ] OR: macos-native-fullscreen [-h|--help] [--window-id ] [--fail-if-noop] on OR: macos-native-fullscreen [-h|--help] [--window-id ] [--fail-if-noop] off """ let macos_native_minimize_help_generated = """ USAGE: macos-native-minimize [-h|--help] [--window-id ] """ let mode_help_generated = """ USAGE: mode [-h|--help] """ let move_mouse_help_generated = """ USAGE: move-mouse [-h|--help] [--fail-if-noop] """ let move_node_to_monitor_help_generated = """ USAGE: move-node-to-monitor [-h|--help] [--window-id ] [--focus-follows-window] [--wrap-around] (left|down|up|right|next|prev) OR: move-node-to-monitor [-h|--help] [--window-id ] [--focus-follows-window] [--fail-if-noop] ... """ let move_node_to_workspace_help_generated = """ USAGE: move-node-to-workspace [-h|--help] [--focus-follows-window] [--wrap-around] [--stdin|--no-stdin] (next|prev) OR: move-node-to-workspace [-h|--help] [--focus-follows-window] [--fail-if-noop] [--window-id ] """ let move_workspace_to_monitor_help_generated = """ USAGE: move-workspace-to-monitor [-h|--help] [--workspace ] [--wrap-around] (left|down|up|right) OR: move-workspace-to-monitor [-h|--help] [--workspace ] [--wrap-around] (next|prev) OR: move-workspace-to-monitor [-h|--help] [--workspace ] ... """ let move_help_generated = """ USAGE: move [-h|--help] [--window-id ] [--boundaries ] [--boundaries-action ] (left|down|up|right) """ let reload_config_help_generated = """ USAGE: reload-config [-h|--help] [--no-gui] [--dry-run] """ let resize_help_generated = """ USAGE: resize [-h|--help] [--window-id ] (smart|smart-opposite|width|height) [+|-] """ let split_help_generated = """ USAGE: split [-h|--help] [--window-id ] (horizontal|vertical|opposite) """ let subscribe_help_generated = """ USAGE: subscribe [-h|--help] [--all] [--no-send-initial] [...] """ let summon_workspace_help_generated = """ USAGE: summon-workspace [-h|--help] [--fail-if-noop] """ let swap_help_generated = """ USAGE: swap [-h|--help] [--window-id ] [--swap-focus] [--wrap-around] (left|down|up|right|dfs-next|dfs-prev) """ let trigger_binding_help_generated = """ USAGE: trigger-binding [-h|--help] --mode """ let volume_help_generated = """ USAGE: volume [-h|--help] (up|down) [--no-gui] OR: volume [-h|--help] (mute-toggle|mute-off|mute-on) [--no-gui] OR: volume [-h|--help] set [--no-gui] """ let workspace_back_and_forth_help_generated = """ USAGE: workspace-back-and-forth [-h|--help] """ let workspace_help_generated = """ USAGE: workspace [-h|--help] [--auto-back-and-forth] [--fail-if-noop] OR: workspace [-h|--help] [--wrap-around] [--stdin|--no-stdin] (next|prev) """ ================================================ FILE: Sources/Common/gitHashGenerated.swift ================================================ // FILE IS GENERATED BY generate.sh public let gitHash = "SNAPSHOT" public let gitShortHash = "SNAPSHOT" ================================================ FILE: Sources/Common/model/AeroSpaceEnvVars.swift ================================================ public let AEROSPACE_WINDOW_ID = "AEROSPACE_WINDOW_ID" public let AEROSPACE_WORKSPACE = "AEROSPACE_WORKSPACE" ================================================ FILE: Sources/Common/model/AxAppThreadToken.swift ================================================ import Foundation @TaskLocal public var axTaskLocalAppThreadToken: AxAppThreadToken? = nil public struct AxAppThreadToken: Sendable, Equatable, CustomStringConvertible { public let pid: pid_t public let idForDebug: String public init(pid: pid_t, idForDebug: String) { self.pid = pid self.idForDebug = idForDebug } public static func == (lhs: Self, rhs: Self) -> Bool { lhs.pid == rhs.pid } public func checkEquals(_ other: AxAppThreadToken?) { check(self == other, "\(self) != \(other.prettyDescription)") } public var description: String { idForDebug } } ================================================ FILE: Sources/Common/model/CardinalDirection.swift ================================================ public enum CardinalDirection: String, CaseIterable, Equatable, Sendable { case left, down, up, right public var orientation: Orientation { self == .up || self == .down ? .v : .h } public var isPositive: Bool { self == .down || self == .right } public var opposite: CardinalDirection { return switch self { case .left: .right case .down: .up case .up: .down case .right: .left } } public var focusOffset: Int { isPositive ? 1 : -1 } public var insertionOffset: Int { isPositive ? 1 : 0 } } ================================================ FILE: Sources/Common/model/CardinalOrDfsDirection.swift ================================================ public enum CardinalOrDfsDirection: Equatable, Sendable { case direction(CardinalDirection) case dfsRelative(DfsNextPrev) } extension CardinalOrDfsDirection: CaseIterable { public static var allCases: [CardinalOrDfsDirection] { CardinalDirection.allCases.map { .direction($0) } + DfsNextPrev.allCases.map { .dfsRelative($0) } } } extension CardinalOrDfsDirection: RawRepresentable { public typealias RawValue = String public init?(rawValue: RawValue) { if let d = CardinalDirection(rawValue: rawValue) { self = .direction(d) } else if let np = DfsNextPrev(rawValue: rawValue) { self = .dfsRelative(np) } else { return nil } } public var rawValue: RawValue { return switch self { case .direction(let d): d.rawValue case .dfsRelative(let np): np.rawValue } } } ================================================ FILE: Sources/Common/model/DfsNextPrev.swift ================================================ public enum DfsNextPrev: String, CaseIterable, Equatable, Sendable { case dfsNext = "dfs-next" case dfsPrev = "dfs-prev" } ================================================ FILE: Sources/Common/model/Init.swift ================================================ nonisolated(unsafe) public var isCli = true var isServer: Bool { !isCli } nonisolated(unsafe) public var terminationHandler: TerminationHandler = EmptyTerminationHandler() struct EmptyTerminationHandler: TerminationHandler { func beforeTermination() {} } @MainActor public protocol TerminationHandler: Sendable { func beforeTermination() async throws } ================================================ FILE: Sources/Common/model/MonitorDescription.swift ================================================ public enum MonitorDescription: Equatable, Sendable { case sequenceNumber(Int) case main case secondary case pattern(String, SendableRegex) public static func == (lhs: MonitorDescription, rhs: MonitorDescription) -> Bool { return switch (lhs, rhs) { case (.main, .main): true case (.secondary, .secondary): true case (.sequenceNumber(let a), .sequenceNumber(let b)): a == b case (.pattern(let a, _), .pattern(let b, _)): a == b default: false } } public static func caseSensitivePattern(_ raw: String) -> MonitorDescription? { (try? SendableRegex(raw)).flatMap { .pattern(raw, $0) } } } public func parseMonitorDescription(_ raw: String) -> Parsed { if let int = Int(raw) { return int >= 1 ? .success(.sequenceNumber(int)) : .failure("Monitor sequence numbers uses 1-based indexing. Values less than 1 are illegal") } if raw == "main" { return .success(.main) } if raw == "secondary" { return .success(.secondary) } return raw.isEmpty ? .failure("Empty string is an illegal monitor description") : parseCaseInsensitiveRegex(raw).map { MonitorDescription.pattern(raw, .init($0)) } } public func parseCaseInsensitiveRegex(_ raw: String) -> Parsed> { Result { try Regex(raw) } .mapError { e in "Can't parse '\(raw)' regex. \(e.localizedDescription)" } .map { $0.ignoresCase() } } /// Circumvent Regex not being Sendable by default public struct SendableRegex: Sendable { nonisolated(unsafe) public let val: Regex init(_ regex: Regex) { self.val = regex } // init(_ str: String) { self.regex = regex } } extension SendableRegex where Output == AnyRegexOutput { public init(_ pattern: String) throws { self = SendableRegex(try Regex(pattern)) } } ================================================ FILE: Sources/Common/model/NextPrev.swift ================================================ public enum NextPrev: String, Equatable, Sendable, CaseIterable { case next, prev } ================================================ FILE: Sources/Common/model/Orientation.swift ================================================ public enum Orientation: Sendable { /// Windows are planced along the **horizontal** line /// x-axis case h /// Windows are planced along the **vertical** line /// y-axis case v } extension Orientation { public var opposite: Orientation { self == .h ? .v : .h } } ================================================ FILE: Sources/Common/model/WorkspaceName.swift ================================================ public struct WorkspaceName: Equatable, Sendable { public let raw: String private init(_ raw: String) { self.raw = raw } public static func parse(_ raw: String) -> Parsed { // reserved names if raw == "focused" || raw == "non-focused" || raw == "visible" || raw == "invisible" || raw == "non-visible" || raw == "active" || raw == "non-active" || raw == "inactive" || raw == "back-and-forth" || raw == "back_and_forth" || raw == "previous" || raw == "prev" || raw == "next" || raw == "monitor" || raw == "workspace" || raw == "monitors" || raw == "workspaces" || raw == "all" || raw == "none" || raw == "mouse" || raw == "target" { return .failure("'\(raw)' is a reserved workspace name") } if raw.isEmpty { return .failure("Empty workspace name is forbidden") } if raw.contains(",") { return .failure("Workspace names are not allowed to contain comma") } if raw.starts(with: "_") { return .failure("Workspace names starting with underscore are reserved for future use") } if raw.starts(with: "-") { // The syntax conflicts with CLI options. E.g. list-windows --workspace -foo return .failure("Workspace names starting with dash are disallowed") } if raw.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { return .failure("Whitespace characters are forbidden in workspace names") } return .success(WorkspaceName(raw)) } } ================================================ FILE: Sources/Common/model/clientServer.swift ================================================ import Foundation // TO EVERYONE REVERSE-ENGINEERING THE PROTOCOL // client-server socket API is not public yet. // Tracking issue for making it public: https://github.com/nikitabobko/AeroSpace/issues/1513 public struct ServerAnswer: Codable, Sendable { public let exitCode: Int32 public let stdout: String public var stderr: String public let serverVersionAndHash: String public init( exitCode: Int32, stdout: String = "", stderr: String = "", serverVersionAndHash: String, ) { self.exitCode = exitCode self.stdout = stdout self.stderr = stderr self.serverVersionAndHash = serverVersionAndHash } } // TO EVERYONE REVERSE-ENGINEERING THE PROTOCOL // client-server socket API is not public yet. // Tracking issue for making it public: https://github.com/nikitabobko/AeroSpace/issues/1513 public struct ClientRequest: Codable, Sendable, ConvenienceCopyable, Equatable { // periphery:ignore - Unused. keep it for API compatibility with old servers for a couple of version public var command: String? = nil public let args: [String] public let stdin: String // Double Optional to encode explicit null into JSON public var windowId: UInt32?? // Please forward AEROSPACE_WINDOW_ID env variable here public var workspace: String?? // Please forward AEROSPACE_WORKSPACE env variable here public init( args: [String], stdin: String, windowId: UInt32?, workspace: String?, ) { self.args = args self.stdin = stdin self.windowId = .some(windowId) self.workspace = .some(workspace) } public static func decodeJson(_ data: Data) -> Result { Result { try JSONDecoder().decode(Self.self, from: data) }.mapError { $0.localizedDescription } } enum CodingKeys: String, CodingKey { case args case stdin case windowId case workspace } public init(from decoder: any Decoder) throws { let data = try ClientRequestData.init(from: decoder) var raw = ClientRequest( args: data.args, stdin: data.stdin, windowId: data.windowId.flatMap { $0 }, workspace: data.workspace.flatMap { $0 }, ) let container = try decoder.container(keyedBy: CodingKeys.self) if !container.contains(.windowId) { raw.windowId = nil } if !container.contains(.workspace) { raw.workspace = nil } self = raw } } private struct ClientRequestData: Codable, Sendable { var args: [String] var stdin: String var windowId: UInt32?? var workspace: String?? } ================================================ FILE: Sources/Common/model/sponsorshipPrompts.swift ================================================ public let sponsorshipPrompts = [ "AeroSpace is a side project\nSponsor if it helps you", "AeroSpace is built in my free time\nYour support means a lot", "AeroSpace is free and open-source\nYour support matters", "Enjoying AeroSpace?\nYou can help keep it going", ] ================================================ FILE: Sources/Common/util/AeroAny.swift ================================================ import Foundation import AppKit public protocol AeroAny {} extension AeroAny { @discardableResult @inlinable public func apply(_ block: (Self) -> Void) -> Self { block(self) return self } @discardableResult @inlinable public func also(_ block: (Self) -> Void) -> Self { block(self) return self } @inlinable public func takeIf(_ predicate: (Self) -> Bool) -> Self? { predicate(self) ? self : nil } @inlinable public func then(_ body: (Self) -> R) -> R { body(self) } } extension Int: AeroAny {} extension String: AeroAny {} extension Character: AeroAny {} extension Regex: AeroAny {} extension Array: AeroAny {} extension URL: AeroAny {} extension CGFloat: AeroAny {} extension AXUIElement: AeroAny {} extension CGPoint: AeroAny {} ================================================ FILE: Sources/Common/util/ArrSlice.swift ================================================ import Foundation public typealias StrArrSlice = ArrSlice // The default ArraySlice that is shiped with Swift stdlib is wrong (my subjective opinion). Their subscript is not zero-based. // Their Slice is not properly encapsulated // // That's why we declare our own ArrSlice public struct ArrSlice: Sequence, AeroAny, ExpressibleByArrayLiteral, RandomAccessCollection { fileprivate let backing: [Element] fileprivate let offsetInBacking: Int /*conforms*/ public let count: Int /*conforms*/ public init(arrayLiteral elements: Element...) { self.init(elements, 0 ..< elements.count) } private init(_ backing: [Element], _ range: Range) { self.backing = range.isEmpty ? [] : backing self.offsetInBacking = range.isEmpty ? 0 : range.startIndex self.count = range.count } public static func new(_ backing: [Element], _ range: Range) -> Self? { range.isSubRange(of: backing.indices) ? .init(backing, range) : nil } public subscript(_ zeroBasedIndex: Int) -> Element { check(indices.contains(zeroBasedIndex)) return backing[zeroBasedIndex + offsetInBacking] } /*conforms*/ public var startIndex: Int { 0 } /*conforms*/ public var endIndex: Int { count } /*conforms*/ public func index(after i: Int) -> Int { i + 1 } public func getOrNil(atIndex index: Int) -> Element? { indices.contains(index) ? self[index] : nil } /*conforms*/ public var indices: Range { 0 ..< count } public func makeIterator() -> some IteratorProtocol { ArrSliceIterator(backing: self) } public func toArray() -> [Element] { Array(self) } } private struct ArrSliceIterator: IteratorProtocol { fileprivate let backing: ArrSlice fileprivate var index: Int = 0 mutating func next() -> Element? { if index < backing.count { let value = backing[index] index += 1 return value } else { return nil } } } extension ArrSlice: Sendable where Element: Sendable {} extension ArrSlice: Equatable where Element: Equatable { public static func == (lhs: Self, rhs: Self) -> Bool { if lhs.count != rhs.count { return false } for i in lhs.indices { if lhs[i] != rhs[i] { return false } } return true } } // periphery:ignore extension Array { public var slice: ArrSlice { slice(0 ..< count).orDie() } public func slice(_ range: PartialRangeFrom) -> ArrSlice? { slice(range.lowerBound ..< count) } public func slice(_ range: PartialRangeUpTo) -> ArrSlice? { slice(0 ..< range.upperBound) } public func slice(_ range: Range) -> ArrSlice? { .new(self, range) } } extension Range { func shift(by value: Int) -> Range { value + startIndex ..< value + endIndex } func isSubRange(of outer: Range) -> Bool { outer.lowerBound <= self.lowerBound && self.upperBound <= outer.upperBound } } // periphery:ignore extension ArrSlice { public func slice(_ range: PartialRangeFrom) -> ArrSlice? { slice(range.lowerBound ..< count) } public func slice(_ range: PartialRangeUpTo) -> ArrSlice? { slice(0 ..< range.upperBound) } public func slice(_ range: Range) -> ArrSlice? { range.isSubRange(of: indices) ? .new(self.backing, range.shift(by: self.offsetInBacking)) : nil } } ================================================ FILE: Sources/Common/util/BoolEx.swift ================================================ import Foundation // https://forums.swift.org/t/using-async-call-in-boolean-expression/52943 // https://github.com/swiftlang/swift/issues/56869 // https://forums.swift.org/t/potential-false-positive-sending-risks-causing-data-races/78859 extension Bool { @inlinable public func andAsync(_ rhs: () async throws -> Bool) async rethrows -> Bool { if self { return try await rhs() } return false } // periphery:ignore @inlinable public func orAsync(_ rhs: () async throws -> Bool) async rethrows -> Bool { if self { return true } return try await rhs() } } ================================================ FILE: Sources/Common/util/CollectionEx.swift ================================================ extension Collection { public func singleOrNil() -> Element? { count == 1 ? first : nil } public func getOrNil(atIndex index: Index) -> Element? { indices.contains(index) ? self[index] : nil } } extension Collection where Index == Int { public func get(wrappingIndex: Int) -> Element? { isEmpty ? nil : self[((wrappingIndex % count) + count) % count] } } ================================================ FILE: Sources/Common/util/ComparableEx.swift ================================================ import AppKit extension Comparable { public func until(incl bound: Self) -> ClosedRange? { self <= bound ? self ... bound : nil } public func until(excl bound: Self) -> Range? { self < bound ? self ..< bound : nil } } ================================================ FILE: Sources/Common/util/ConvenienceCopyable.swift ================================================ public protocol ConvenienceCopyable {} extension ConvenienceCopyable { public consuming func copy(_ key: WritableKeyPath, _ value: T) -> Self { self[keyPath: key] = value return self } } ================================================ FILE: Sources/Common/util/DictionaryEx.swift ================================================ extension Dictionary { @inlinable public func partition(_ predicate: (Dictionary.Element) throws -> Bool) rethrows -> ([Key: Value], [Key: Value]) { var matching = [Key: Value]() var nonMatching = [Key: Value]() for element in self { if try predicate(element) { matching[element.key] = element.value } else { nonMatching[element.key] = element.value } } return (matching, nonMatching) } } ================================================ FILE: Sources/Common/util/EquatableNoop.swift ================================================ public struct EquatableNoop: Equatable { public var value: Value public init(_ value: Value) { self.value = value } public static func == (lhs: EquatableNoop, rhs: EquatableNoop) -> Bool { true } } extension EquatableNoop: Sendable where Value: Sendable {} ================================================ FILE: Sources/Common/util/JsonEncoderEx.swift ================================================ import Foundation extension JSONEncoder { public static var aeroSpaceDefault: JSONEncoder { let encoder = JSONEncoder() encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes, .sortedKeys] return encoder } public func encodeToString(_ value: Encodable) -> String? { guard let data = Result(catching: { try encode(value) }).getOrNil() else { return nil } return String(data: data, encoding: .utf8) } } ================================================ FILE: Sources/Common/util/Lateinit.swift ================================================ // "Happy path" Optional public enum Lateinit { case initialized(T) case uninitialized public var val: T { switch self { case .initialized(let value): return value case .uninitialized: die("Property is not initialized") } } public var isInitialized: Bool { return switch self { case .initialized: true case .uninitialized: false } } } extension Lateinit: Equatable where T: Equatable { public static func == (lhs: Self, rhs: Self) -> Bool { lhs.isInitialized && rhs.isInitialized && lhs.val == rhs.val || lhs.isInitialized == rhs.isInitialized } } extension Lateinit: Sendable where T: Sendable {} ================================================ FILE: Sources/Common/util/MainActorEx.swift ================================================ import Foundation extension MainActor { public static func checkIsolated(_ operation: @MainActor () throws -> T, file: StaticString = #fileID, line: UInt = #line) rethrows -> T where T: Sendable { check(Thread.isMainThread) return try assumeIsolated(operation, file: file, line: line) } } ================================================ FILE: Sources/Common/util/NWConnectionEx.swift ================================================ import Network import Foundation extension NWConnection { public func writeAtomic(_ msg: Codable, _ encoder: JSONEncoder = JSONEncoder()) async -> ((), error: NWError?) { let payload = Result { try encoder.encode(msg) }.getOrDie() var data = withUnsafeBytes(of: UInt32(payload.count)) { Data($0) } check(data.count == 4) data.append(payload) return await withCheckedContinuation { cont in send(content: data, completion: .contentProcessed { error in if let error { cont.resume(returning: ((), error)) } else { cont.resume(returning: ((), nil)) } }) } } public func startBlocking() async -> ((), error: NWError?) { await withCheckedContinuation { cont in let isDone = IsDone() stateUpdateHandler = { state in Task { let error: NWError? switch state { case .cancelled, .preparing, .setup: return case .ready: error = nil case .failed(let e), .waiting(let e): error = e @unknown default: die("Unknown NWConnection.State: \(state)") } // Make sure to resume continuation only once if await isDone.markAsDone().wasAlreadyDone { return } self.stateUpdateHandler = nil cont.resume(returning: ((), error)) } } start(queue: .global()) } } private func read(bytes size: Int) async -> Result { var data = Data(capacity: size) while data.count < size { let remaining = size - data.count let chunk: Result = await withCheckedContinuation { cont in receive(minimumIncompleteLength: remaining, maximumLength: remaining) { data, context, isComplete, error in if let error { cont.resume(returning: .failure(error)) } else { cont.resume(returning: .success(data ?? Data())) } } } switch chunk { case .success(let chunk): data.append(chunk) case .failure: return chunk } } return .success(data) } public func readTillError() async { while true { let isError = await withCheckedContinuation { cont in receive(minimumIncompleteLength: 1, maximumLength: Int.max) { data, context, isComplete, error in cont.resume(returning: error != nil || data == nil || data?.count == 0) } } if isError { return } } } public func readNonAtomic() async -> Result { switch await read(bytes: 4) { case .success(let header): let count = header.withUnsafeBytes { $0.load(as: UInt32.self) } return await read(bytes: Int(count)) case .failure(let e): return .failure(e) } } } private actor IsDone { private var isDone: Bool = false func markAsDone() -> (wasAlreadyDone: Bool, ()) { let old = isDone isDone = true return (old, ()) } } ================================================ FILE: Sources/Common/util/Nullable.swift ================================================ /// Like Swift's built-in Optional but avoids implicit nil coercions public enum Nullable { case just(T) case null public var valueOrNil: T? { switch self { case .just(let value): value case .null: nil } } public var isNull: Bool { valueOrNil == nil } } ================================================ FILE: Sources/Common/util/OptionalEx.swift ================================================ extension Optional { public func orDie( _ message: String = "", file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) -> Wrapped { self ?? dieT("orDie: " + message, file: file, line: line, column: column, function: function) } public func orFailure(_ or: @autoclosure () -> F) -> Result { if let ok = self { return .success(ok) } else { return .failure(or()) } } public func flatMapAsync(_ transform: (Wrapped) async throws -> U?) async rethrows -> U? { if let ok = self { return try await transform(ok) } else { return nil } } public func asList() -> [Wrapped] { if let ok = self { return [ok] } else { return [] } } public var prettyDescription: String { if let unwrapped = self { return String(describing: unwrapped) } return "nil" } } ================================================ FILE: Sources/Common/util/ResultEx.swift ================================================ extension Result { public init(catching body: () async throws(Failure) -> Success) async { do { self = .success(try await body()) } catch { self = .failure(error) } } public func getOrNil(appendErrorTo errors: inout [Failure]) -> Success? { switch self { case .success(let success): return success case .failure(let error): errors += [error] return nil } } public func filter(_ failure: @autoclosure () -> Failure, _ predicate: (Success) -> Bool) -> Self { flatMap { succ in predicate(succ) ? .success(succ) : .failure(failure()) } } public func getOrNil() -> Success? { return switch self { case .success(let success): success case .failure: nil } } public var failureOrNil: Failure? { return switch self { case .success: nil case .failure(let f): f } } public var isSuccess: Bool { switch self { case .success: true case .failure: false } } } extension Result { @discardableResult public func getOrDie( _ msgPrefix: String = "", file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) -> Success { switch self { case .success(let suc): return suc case .failure(let e): die(msgPrefix + e.localizedDescription, file: file, line: line, column: column, function: function) } } } ================================================ FILE: Sources/Common/util/SequenceEx.swift ================================================ // periphery:ignore:all import AppKit import OrderedCollections extension Sequence { public func filterNotNil() -> [Unwrapped] where Element == Unwrapped? { compactMap { $0 } } public func filterIsInstance(of _: R.Type) -> [R] { var result: [R] = [] for elem in self { if let elemR = elem as? R { result.append(elemR) } } return result } public var first: Element? { var iter = makeIterator() return iter.next() } public func mapAllOrFailure(_ transform: (Self.Element) -> Result) -> Result<[T], E> { var result: [T] = [] for element in self { switch transform(element) { case .success(let element): result.append(element) case .failure(let errors): return .failure(errors) } } return .success(result) } public func mapAllOrFailures(_ transform: (Self.Element) -> Result) -> Result<[T], [E]> { var result: [T] = [] var errors: [E] = [] for element in self { switch transform(element) { case .success(let element): result.append(element) case .failure(let error): errors.append(error) } } return errors.isEmpty ? .success(result) : .failure(errors) } @inlinable public func minByOrDie(_ selector: (Self.Element) -> some Comparable) -> Self.Element { minBy(selector) ?? dieT("Empty sequence") } @inlinable public func minBy(_ selector: (Self.Element) -> some Comparable) -> Self.Element? { self.min(by: { a, b in selector(a) < selector(b) }) } @inlinable public func maxByOrDie(_ selector: (Self.Element) -> some Comparable) -> Self.Element? { self.maxBy(selector) ?? dieT("Empty sequence") } @inlinable public func maxBy(_ selector: (Self.Element) -> some Comparable) -> Self.Element? { self.max(by: { a, b in selector(a) < selector(b) }) } @inlinable public func sortedBy(_ selector: (Self.Element) -> some Comparable) -> [Self.Element] { sorted(by: { a, b in selector(a) < selector(b) }) } @inlinable public func sortedBy(_ selectors: [(Self.Element) -> some Comparable]) -> [Self.Element] { sorted(by: { a, b in for selector in selectors { let a = selector(a) let b = selector(b) if a < b { return true } if a > b { return false } } return false }) } public func sumOfDouble(_ selector: (Self.Element) -> Double) -> Double { var result: Double = 0 for elem in self { result += selector(elem) } return result } public func sumOfInt(_ selector: (Self.Element) -> Int) -> Int { var result: Int = 0 for elem in self { result += selector(elem) } return result } public func grouped(by criterion: (_ transforming: Element) -> Group) -> [Group: [Element]] { Dictionary(grouping: self, by: criterion) } public var withIndex: [(index: Int, value: Element)] { var index = -1 return map { index += 1 return (index, $0) } } } extension Sequence where Self.Element: Comparable { public func minOrDie() -> Self.Element { self.min() ?? dieT("Empty sequence") } public func maxOrDie() -> Self.Element { self.max() ?? dieT("Empty sequence") } } extension Sequence where Element: Hashable { public func toSet() -> Set { Set(self) } public func toOrderedSet() -> OrderedSet { OrderedSet(self) } } ================================================ FILE: Sources/Common/util/StringEx.swift ================================================ public typealias Parsed = Result extension String: @retroactive Error {} // Make it possible to use String in Result. todo migrate to self written Result monad extension Array: @retroactive Error where Element: Error {} // Make it possible to use [String] in Result. todo migrate to self written Result monad extension String { public func trim() -> String { self.trimmingCharacters(in: .whitespacesAndNewlines) } public func prefixLines(with: String) -> String { split(separator: "\n", omittingEmptySubsequences: false).map { with + $0 }.joined(separator: "\n") } public func quoted(with char: String) -> String { char + self + char } public var singleQuoted: String { "'" + self + "'" } public var doubleQuoted: String { "\"" + self + "\"" } } extension [String] { public func joinErrors() -> String { // todo reuse in config parsing? map { (error: String) -> String in error.split(separator: "\n").enumerated() .map { (i, line) in i == 0 ? "ERROR: " + line : " " + line } .joined(separator: "\n") } .joined(separator: "\n") } public func joinTruncating(separator: String, length maxLength: Int, trailing: String = "…") -> String { if isEmpty { return "" } var remainingLen = maxLength let separatorCount = separator.count var result: String = first.orDie() for _elem in self.dropFirst() { let elemCount = separatorCount + _elem.count if remainingLen < elemCount / 2 { return result + separator + trailing } let elem = separator + _elem if elemCount < remainingLen { result += elem remainingLen -= elemCount } else { return result + elem.prefix(remainingLen) + trailing } } return result } } extension [[String]] { public func toPaddingTable(columnSeparator: String = " | ") -> [String] { let pads: [Int] = transposed().map { column in column.map { $0.count }.max().orDie() } return self.map { (row: [String]) in zip(row.enumerated(), pads) .map { (elem: (Int, String), pad: Int) in elem.0 != row.count - 1 ? elem.1.padding(toLength: pad, withPad: " ", startingAt: 0) : elem.1 } .joined(separator: columnSeparator) } } } extension Array { // todo move to ArrayEx.swift public func transposed() -> [[T]] where Self.Element == [T] { if isEmpty { return [] } let table: [[T]] = self var result: [[T]] = [] for columnIndex in 0... { if columnIndex < table.first.orDie().count { result += [table.map { row in row.getOrNil(atIndex: columnIndex).orDie() }] } else { break } } return result } } extension String { public func interpolate(with variables: [String: String]) -> Result { interpolationTokens(interpolationChar: "$") .mapError { [$0] } .flatMap { tokens in tokens.mapAllOrFailures { token in switch token { case .literal(let literal): .success(literal) case .interVar(let value): variables[value].flatMap(Result.success) ?? .failure("Env variable '\(value)' isn't presented in AeroSpace.app env vars, " + "or not available for interpolation (because it's mutated)") } } } .map { $0.joined(separator: "") } } public func interpolationTokens(interpolationChar: Character) -> Result<[StringInterToken], String> { var mode: InterpolationParserState = .stringLiteral var result: [StringInterToken] = [] var literal: String = "" for char: Character? in (Array(self) + [nil]) { switch (mode, char) { // State machine case (.stringLiteral, interpolationChar): mode = .interpolationCharEncountered case (.stringLiteral, _): if let char { literal.append(char) } else { result.append(.literal(literal)) } case (.interpolationCharEncountered, "{"): mode = .interpolatedValue("") result.append(.literal(literal)) literal = "" case (.interpolationCharEncountered, interpolationChar): literal.append(interpolationChar) case (.interpolationCharEncountered, _): literal.append(interpolationChar) if let char { literal.append(char) } else { result.append(.literal(literal)) } mode = .stringLiteral case (.interpolatedValue(let value), "}"): result.append(.interVar(value)) mode = .stringLiteral case (.interpolatedValue(let value), "{"): return .failure("Can't parse '\(value + "{")' inside interpolation (Open curly brace is invalid character)") case (.interpolatedValue(let value), interpolationChar): return .failure("Can't parse '\(value + .init(interpolationChar))' inside interpolation ('\(interpolationChar)' is disallowed character)") case (.interpolatedValue(let value), _): if let char { mode = .interpolatedValue(value + .init(char)) } else { return .failure("Unbalanced curly braces") } } } return .success(result.filter { $0 != .literal("") }) } } public enum StringInterToken: Equatable, Sendable { case literal(String) case interVar(String) // "interpolation variable" } private enum InterpolationParserState { case stringLiteral, interpolationCharEncountered case interpolatedValue(String) } ================================================ FILE: Sources/Common/util/StringLogicalSegments.swift ================================================ public typealias StringLogicalSegments = [StringLogicalSegment] extension StringLogicalSegments { public static func < (lhs: Self, rhs: Self) -> Bool { for (a, b) in zip(lhs, rhs) { if a < b { return true } if a > b { return false } } if lhs.count != rhs.count { return lhs.count < rhs.count } return false } } public enum StringLogicalSegment: Comparable, Equatable, Sendable { case string(String) case number(Int) public static func < (lhs: Self, rhs: Self) -> Bool { switch (lhs, rhs) { case (.string(let a), .string(let b)): a < b case (.number(let a), .number(let b)): a < b case (.number, _): true case (.string, _): false } } } extension String { public func toLogicalSegments() -> StringLogicalSegments { var currentSegment: String = "" var isPrevNumber: Bool = false // Initial value doesn't matter var result: [String] = [] for char in self { let isCurNumber = Int(char.description) != nil if isCurNumber != isPrevNumber && !currentSegment.isEmpty { result.append(currentSegment) currentSegment = "" } currentSegment.append(char) isPrevNumber = isCurNumber } if !currentSegment.isEmpty { result.append(currentSegment) } return result.map { Int($0).flatMap(StringLogicalSegment.number) ?? .string($0) } } } ================================================ FILE: Sources/Common/util/commonUtil.swift ================================================ import AppKit import Darwin import Foundation public let socketPath = "/tmp/\(aeroSpaceAppId)-\(unixUserName).sock" public let unixUserName = NSUserName() public let mainModeId = "main" @TaskLocal public var refreshSessionEvent: RefreshSessionEvent? = nil @TaskLocal private var recursionDetectorDuringTermination = false public func dieT( _ __message: String = "", file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) -> T { let _message = __message.contains("\n") ? "\n" + __message.prefixLines(with: " ") : __message let thread = Thread.current let message = """ Please report to: https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs Please describe what you did to trigger this error Message: \(_message) Version: \(aeroSpaceAppVersion) Git hash: \(gitHash) refreshSessionEvent: \(refreshSessionEvent.prettyDescription) Date: \(Date.now) Thread name: \(thread.name.prettyDescription) Is main thread: \(thread.isMainThread) axTaskLocalAppThreadToken: \(axTaskLocalAppThreadToken.prettyDescription) macOS version: \(ProcessInfo().operatingSystemVersionString) Coordinate: \(file):\(line):\(column) \(function) recursionDetectorDuringTermination: \(recursionDetectorDuringTermination) cli: \(isCli) Monitor count: \(NSScreen.screens.count) Displays have separate spaces: \(NSScreen.screensHaveSeparateSpaces) Stacktrace: \(getStringStacktrace()) """ if !isUnitTest && isServer { showMessageInGui( filenameIfConsoleApp: recursionDetectorDuringTermination ? "aerospace-runtime-error-recursion.txt" : "aerospace-runtime-error.txt", title: "AeroSpace Runtime Error", message: message, ) } if !recursionDetectorDuringTermination { let semaphore = DispatchSemaphore(value: 0) Task { defer { semaphore.signal() } try await $recursionDetectorDuringTermination.withValue(true) { try await terminationHandler.beforeTermination() } } semaphore.wait() } fatalError("\n" + message) } public enum RefreshSessionEvent: Sendable, CustomStringConvertible { case configAutoReload case globalObserver(String) case globalObserverLeftMouseUp case menuBarButton case hotkeyBinding case startup case socketServer(any CmdArgs) case resetManipulatedWithMouse case ax(String) case onFocusedMonitorChanged case onFocusChanged case onModeChanged public var isStartup: Bool { if case .startup = self { return true } else { return false } } public var description: String { switch self { case .ax(let str): "ax(\(str))" case .configAutoReload: "configAutoReload" case .globalObserver(let str): "globalObserver(\(str))" case .globalObserverLeftMouseUp: "globalObserverLeftMouseUp" case .hotkeyBinding: "hotkeyBinding" case .menuBarButton: "menuBarButton" case .resetManipulatedWithMouse: "resetManipulatedWithMouse" case .socketServer(let args): "socketServer: \(args)" case .startup: "startup" case .onFocusedMonitorChanged: "onFocusedMonitorChanged" case .onFocusChanged: "onFocusChanged" case .onModeChanged: "onModeChanged" } } } public func throwT(_ error: Error) throws -> T { throw error } public func getStringStacktrace() -> String { Thread.callStackSymbols.joined(separator: "\n") } @inlinable public func die( _ message: String = "", file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) -> Never { dieT(message, file: file, line: line, column: column, function: function) } public func check( _ condition: Bool, _ message: @autoclosure () -> String = "", file: StaticString = #fileID, line: Int = #line, column: Int = #column, function: String = #function, ) { if !condition { die(message(), file: file, line: line, column: column, function: function) } } public var isUnitTest: Bool { NSClassFromString("XCTestCase") != nil } extension CaseIterable where Self: RawRepresentable, RawValue == String { public static var cliArgsCases: [String] { allCases.map(\.rawValue) } public static var unionLiteral: String { cliArgsCases.joinedCliArgs } } extension [String] { public var joinedCliArgs: String { "(" + self.joined(separator: "|") + ")" } } extension Int { public func toDouble() -> Double { Double(self) } } public func + (lhs: [K: V], rhs: [K: V]) -> [K: V] { lhs.merging(rhs) { _, r in r } } extension String { public func removePrefix(_ prefix: String) -> String { hasPrefix(prefix) ? String(dropFirst(prefix.count)) : self } public func prependLines(_ prefix: String) -> String { split(separator: "\n").map { prefix + $0 }.joined(separator: "\n") } } extension Bool { /// Implication /// | a | b | a.implies(b) | /// |-------|-------|--------------| /// | false | false | true | /// | false | true | true | /// | true | false | false | /// | true | true | true | public func implies(_ mustHold: @autoclosure () -> Bool) -> Bool { !self || mustHold() } } extension URL { public func open(with url: URL) { NSWorkspace.shared.open([self], withApplicationAt: url, configuration: NSWorkspace.OpenConfiguration()) } } public func eprint(_ msg: String) { fputs(msg + "\n", stderr) } public func exit(_ exitCode: Int32, out: String? = nil, err: String? = nil) -> Never { exitT(exitCode, out: out, err: err) } public func exitT(_ exitCode: Int32, out: String? = nil, err: String? = nil) -> T { if let out { print(out) } if let err { eprint(err) } exit(exitCode) } @inlinable public func allowOnlyCancellationError(_ block: () async throws -> sending T) async throws -> sending T { do { return try await block() } catch let e as CancellationError { throw e } catch { die("throws must only be used for CancellationError") } } ================================================ FILE: Sources/Common/util/showMessageInGui.swift ================================================ import Foundation // todo refactor. showMessageInGui in common code looks weird func showMessageInGui(filenameIfConsoleApp: String, title: String, message: String) { let titleAndMessage = "##### \(title) #####\n\n" + message if isCli { print(titleAndMessage) } else { let cachesDir = URL(filePath: "/tmp/bobko.aerospace/") Result { try FileManager.default.createDirectory(at: cachesDir, withIntermediateDirectories: true) }.getOrDie() let file = cachesDir.appending(component: filenameIfConsoleApp) Result { try (titleAndMessage + "\n").write(to: file, atomically: true, encoding: .utf8) }.getOrDie() file.absoluteURL.open(with: URL(filePath: "/System/Applications/Utilities/Console.app")) } } ================================================ FILE: Sources/Common/versionGenerated.swift ================================================ // FILE IS GENERATED BY generate.sh public let aeroSpaceAppVersion = "0.0.0-SNAPSHOT" ================================================ FILE: Sources/PrivateApi/include/module.modulemap ================================================ module PrivateApi { header "private.h" export * } ================================================ FILE: Sources/PrivateApi/include/private.h ================================================ #ifndef private_header_h #define private_header_h #import // Potential alternative 1? // func allWindowsOnCurrentMacOsSpace() { // let options = CGWindowListOption(arrayLiteral: .excludeDesktopElements, .optionOnScreenOnly) // let windowsListInfo = CGWindowListCopyWindowInfo(options, CGWindowID(0)) // let infoList = windowsListInfo as! [[String:Any]] // let windows = infoList.filter { $0["kCGWindowLayer"] as! Int == 0 } // print(windows.count) // for window in windows { // print(window) // print("Name: \(window["kCGWindowOwnerName"].unsafelyUnwrapped)") // print("PID: \(window["kCGWindowOwnerPID"].unsafelyUnwrapped)") // print("window ID: \(window["kCGWindowNumber"])") // print("---") // } // } // // Alternative 2: // @_silgen_name("_AXUIElementGetWindow") // @discardableResult // func _AXUIElementGetWindow(_ axUiElement: AXUIElement, _ id: inout CGWindowID) -> AXError AXError _AXUIElementGetWindow(AXUIElementRef element, uint32_t *identifier); #endif ================================================ FILE: Sources/PrivateApi/include/private.m ================================================ // This file exists purely because xcode doesn't like header only targets, SPM is fine with them #import "private.h" ================================================ FILE: axDumps/1password.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1118.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1727.000000 y:1089.000000 w:1698.000000 h:1078.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1682}", "AXPosition" : " {value = x:1727.000000 y:1089.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1698.000000 h:1078.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Account Name — All Items — 1Password", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=1682}", "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=1682}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "1Password", "AXWindows" : [ "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.1password.1password", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/1Password.app/Contents/MacOS/1Password", "Aero.App.version" : "8.11.4.360922", "Aero.App.versionShort" : "8.11.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.axWindowId" : 3746, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "Random" } ================================================ FILE: axDumps/1password_large_type_window.json5 ================================================ // I have no idea what does 'large type window' mean. I took it from: https://github.com/nikitabobko/AeroSpace/discussions/1616 { "AXActivationPoint" : " {value = x:1797.000000 y:1103.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1727.000000 y:1089.000000 w:1698.000000 h:1078.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1682}", "AXPosition" : " {value = x:1727.000000 y:1089.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1698.000000 h:1078.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Item Name — field name — 1Password", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=1682}", "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=1682}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "1Password", "AXWindows" : [ "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.1password.1password", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/1Password.app/Contents/MacOS/1Password", "Aero.App.version" : "8.11.4.360922", "Aero.App.versionShort" : "8.11.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 3789, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "Random" } ================================================ FILE: axDumps/1password_mini_window.json5 ================================================ // I have no idea what does 'mini window' mean. I took it from: https://github.com/nikitabobko/AeroSpace/discussions/1616 { "AXActivationPoint" : " {value = x:1797.000000 y:1103.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1727.000000 y:1089.000000 w:409.000000 h:532.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1682}", "AXPosition" : " {value = x:1727.000000 y:1089.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1682}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:409.000000 h:532.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Item Name — 1Password", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=1682}", "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=1682}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "1Password", "AXWindows" : [ "AXUIElement(AxWindowId=3798, title=\"Item Name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3789, title=\"Item Name — field name — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=3746, title=\"Account Name — All Items — 1Password\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.1password.1password", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/1Password.app/Contents/MacOS/1Password", "Aero.App.version" : "8.11.4.360922", "Aero.App.versionShort" : "8.11.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 3798, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ { "commands" : "layout floating", "matcher" : "appId=\"com.1password.1password\", windowTitleRegexSubstring=Regex" } ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "Random" } ================================================ FILE: axDumps/about_this_mac.json5 ================================================ // top left corner -> apple logo -> about this mac { "AXActivationPoint" : " {value = x:790.000000 y:217.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:760.000000 y:201.000000 w:280.000000 h:487.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93549}", "AXPosition" : " {value = x:760.000000 y:201.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93549}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:280.000000 h:487.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=4729, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93549}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "System Information", "AXWindows" : [ "AXUIElement(AxWindowId=4729, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.SystemProfiler", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/Utilities/System%20Information.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/Utilities/System%20Information.app/Contents/MacOS/System%20Information", "Aero.App.pid" : 93549, "Aero.App.version" : "915", "Aero.App.versionShort" : "11.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 4729, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/alacritty_decorations_buttonless.json5 ================================================ // ~/.alacritty.toml: // [window] // decorations = "Buttonless" { "AXActivationPoint" : " {value = x:10.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1835}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "ta ~", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1835}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Alacritty", "AXWindows" : [ "AXUIElement(AxWindowId=197, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.alacritty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.version" : "1", "Aero.App.versionShort" : "0.15.1", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : "197", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/apple_calendar.json5 ================================================ { "AXActivationPoint" : " {value = x:610.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:600.000000 y:40.000000 w:680.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "CALMainWindow", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93759}", "AXPosition" : " {value = x:600.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=93759}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=93759}\";\n SectionUniqueID = AXSearch;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=93759}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:680.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Calendar", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXDescription" : "previous year", "AXEnabled" : 1, "AXIdentifier" : "previous year", "AXParent" : "AXUIElement(AxWindowId=4734, title=nil, role=\"AXGroup\", subrole=nil)", "AXRole" : "AXButton", "AXTitle" : "", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93759}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Calendar", "AXWindows" : [ "AXUIElement(AxWindowId=4734, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.iCal", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/Calendar.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/Calendar.app/Contents/MacOS/Calendar", "Aero.App.pid" : 93759, "Aero.App.version" : "3036.1.8", "Aero.App.versionShort" : "16.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4734, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/apple_calendar_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:615.000000 y:185.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:585.000000 y:169.000000 w:629.000000 h:646.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93809}", "AXPosition" : " {value = x:585.000000 y:169.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=93809}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=93809}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:629.000000 h:646.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "General", "AXTitleUIElement" : "AXUIElement(AxWindowId=4740, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93809}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Calendar", "AXWindows" : [ "AXUIElement(AxWindowId=4740, title=\"General\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=4739, title=\"Calendar\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.iCal", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/Calendar.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/Calendar.app/Contents/MacOS/Calendar", "Aero.App.pid" : 93809, "Aero.App.version" : "3036.1.8", "Aero.App.versionShort" : "16.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 4740, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/apple_followup_sign_in_to_a_new_device_confirmation.json5 ================================================ // firefox -> icloud.com -> sign in { "AXActivationPoint" : " {value = x:670.000000 y:428.000000 type = kAXValueCGPointType}", "AXCancelButton" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:78", "AXParent" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Do Not Allow", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXCloseButton" : null, "AXDefaultButton" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:69", "AXParent" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Allow", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:660.000000 y:412.000000 w:480.000000 h:375.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:24", "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93865}", "AXPosition" : " {value = x:660.000000 y:412.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93865}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:480.000000 h:375.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=4744, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:78", "AXParent" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Do Not Allow", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93865}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "FollowUpUI", "AXWindows" : [ "AXUIElement(AxWindowId=4744, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.FollowUpUI", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///System/Library/PrivateFrameworks/CoreFollowUp.framework/Versions/A/Resources/FollowUpUI.app/", "Aero.App.nsApp.execPath" : "file:///System/Library/PrivateFrameworks/CoreFollowUp.framework/Versions/A/Resources/FollowUpUI.app/Contents/MacOS/FollowUpUI", "Aero.App.pid" : 93865, "Aero.App.version" : "281.1.5", "Aero.App.versionShort" : "1.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 4744, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 25, "Aero.workspace" : null } ================================================ FILE: axDumps/apple_mail.json5 ================================================ { "AXActivationPoint" : " {value = x:910.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : { "AXEnabled" : 1, "AXIdentifier" : "Mail.categories.onboarding.button.tryCategories", "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=5005, title=nil, role=\"AXGroup\", subrole=nil)", "AXRole" : "AXButton", "AXTitle" : "Try Categories", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "Mail.messageViewer.window.1", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=84847}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = \"Content Navigator\";\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXContentNavigator;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXSearch;\n}", "{\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=84847}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Inbox – REDACTED – Primary · 64 messages", "AXTitleUIElement" : "AXUIElement(AxWindowId=5005, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=5005, title=nil, role=\"AXTable\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=84847}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Mail", "AXWindows" : [ "AXUIElement(AxWindowId=5005, title=\"Inbox – REDACTED – Primary · 64 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.mail", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Applications/Mail.app/Contents/MacOS/Mail", "Aero.App.version" : "3826.600.51.1.1", "Aero.App.versionShort" : "16.0", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 5005, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/apple_mail_new_email.json5 ================================================ { "AXActivationPoint" : " {value = x:610.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:600.000000 y:40.000000 w:600.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "_NS:41", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93925}", "AXPosition" : " {value = x:600.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=93925}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=93925}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:600.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "New Message", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4751, title=nil, role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93925}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Mail", "AXWindows" : [ "AXUIElement(AxWindowId=4751, title=\"New Message\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=4750, title=\"Inbox – REDACTED@jetbrains.com – 71 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.mail", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/Mail.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/Mail.app/Contents/MacOS/Mail", "Aero.App.pid" : 93925, "Aero.App.version" : "3864.200.81.1.6", "Aero.App.versionShort" : "16.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4751, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/apple_mail_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:690.000000 y:274.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:660.000000 y:258.000000 w:775.000000 h:670.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:178", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93925}", "AXPosition" : " {value = x:660.000000 y:258.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=93925}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=93925}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:775.000000 h:670.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Composing", "AXTitleUIElement" : "AXUIElement(AxWindowId=4752, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93925}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Mail", "AXWindows" : [ "AXUIElement(AxWindowId=4752, title=\"Composing\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=4750, title=\"Inbox – REDACTED@jetbrains.com – 71 messages\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.mail", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/Mail.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/Mail.app/Contents/MacOS/Mail", "Aero.App.pid" : 93925, "Aero.App.version" : "3864.200.81.1.6", "Aero.App.versionShort" : "16.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized", "Aero.axWindowId" : 4752, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/archiveutility.json5 ================================================ // E.g. you can download Xcode. It takes a while to unpack it, so you have a plenty of time to capture the AX of the window { "AXActivationPoint" : " {value = x:1869.000000 y:1155.000000 type = kAXValueCGPointType}", "AXCancelButton" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:174", "AXParent" : "AXUIElement(AxWindowId=16775, title=nil, role=\"AXScrollArea\", subrole=nil)", "AXRole" : "AXButton", "AXTitle" : "Cancel", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:1799.000000 y:1141.000000 w:378.000000 h:134.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:8", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=96527}", "AXPosition" : " {value = x:1799.000000 y:1141.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:378.000000 h:134.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Archive Utility", "AXTitleUIElement" : "AXUIElement(AxWindowId=16775, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=96527}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Archive Utility", "AXWindows" : [ "AXUIElement(AxWindowId=16775, title=\"Archive Utility\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.archiveutility", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Library/CoreServices/Applications/Archive%20Utility.app/Contents/MacOS/Archive%20Utility", "Aero.App.version" : "162", "Aero.App.versionShort" : "10.15", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 16775, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "W" } ================================================ FILE: axDumps/brave.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "chrome://newtab/", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:40.000000 w:1800.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23662}", "AXPosition" : " {value = x:0.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23662}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=23662}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "New tab - Brave", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2607, title=\"New Tab\", role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23662}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Brave", "AXWindows" : [ "AXUIElement(AxWindowId=2607, title=\"New tab - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.brave.Browser", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Brave%20Browser.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Brave%20Browser.app/Contents/MacOS/Brave%20Browser", "Aero.App.pid" : 23662, "Aero.App.version" : "184.135", "Aero.App.versionShort" : "142.1.84.135", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2607, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/brave_pip.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1028.000000 y:612.000000 w:635.000000 h:357.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23662}", "AXPosition" : " {value = x:1028.000000 y:612.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23662}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=23662}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:635.000000 h:357.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Picture-in-picture", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23662}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Brave", "AXWindows" : [ "AXUIElement(AxWindowId=2626, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=2607, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube – Video playing in picture-in-picture mode - Brave\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.brave.Browser", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Brave%20Browser.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Brave%20Browser.app/Contents/MacOS/Brave%20Browser", "Aero.App.pid" : 23662, "Aero.App.version" : "184.135", "Aero.App.versionShort" : "142.1.84.135", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXSize", "Aero.axWindowId" : 2626, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/calculator.json5 ================================================ { "AXActivationPoint" : " {value = x:759.000000 y:606.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:749.000000 y:592.000000 w:198.000000 h:350.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "main", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=45807}", "AXPosition" : " {value = x:749.000000 y:592.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=45807}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=45807}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=45807}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:198.000000 h:350.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Calculator", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14642, title=nil, role=\"AXGroup\", subrole=\"AXHostingView\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=45807}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Calculator", "AXWindows" : [ "AXUIElement(AxWindowId=14642, title=\"Calculator\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.calculator", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Applications/Calculator.app/Contents/MacOS/Calculator", "Aero.App.version" : "224", "Aero.App.versionShort" : "11.0", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14642, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/choose_1_5_0.json5 ================================================ // choose -v # => 1.5.0 { "AXActivationPoint" : " {value = x:510.000000 y:240.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:500.000000 y:226.000000 w:800.000000 h:387.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93987}", "AXPosition" : " {value = x:500.000000 y:226.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93987}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=93987}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:800.000000 h:387.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=4753, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4753, title=nil, role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4753, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4753, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=93987}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "choose", "AXWindows" : [ "AXUIElement(AxWindowId=4753, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : null, "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///opt/homebrew/Cellar/choose-gui/1.5.0/bin/choose/", "Aero.App.nsApp.execPath" : "file:///opt/homebrew/bin/choose", "Aero.App.pid" : 93987, "Aero.App.version" : null, "Aero.App.versionShort" : null, "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 4753, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/chrome.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "chrome://newtab/", "AXFocused" : 0, "AXFrame" : " {value = x:1799.000000 y:1128.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=34617}", "AXPosition" : " {value = x:1799.000000 y:1128.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "New Tab - Google Chrome", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14057, title=\"\", role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=34617}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7049.116", "Aero.App.versionShort" : "135.0.7049.116", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14057, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ { "commands" : "move-node-to-workspace F", "matcher" : "appId=\"com.google.Chrome\"" } ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/chrome_choose_what_to_share_popup.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:146.000000 y:127.000000 w:608.000000 h:579.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=552}", "AXPosition" : " {value = x:146.000000 y:127.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=552}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=552}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:608.000000 h:579.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "Choose what to share with meet.google.com", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXAccessKey" : null, "AXBlockQuoteLevel" : 0, "AXColumnHeaderUIElements" : null, "AXCustomContent" : null, "AXDOMClassList" : [ "MdTextButton" ], "AXDOMIdentifier" : "", "AXDescription" : "Cancel", "AXElementBusy" : 0, "AXEnabled" : 1, "AXInsertionPointLineNumber" : 0, "AXInvalid" : "false", "AXNumberOfCharacters" : 0, "AXParent" : "AXUIElement(AxWindowId=913, title=\"\", role=\"AXGroup\", subrole=nil)", "AXPlaceholderValue" : null, "AXRequired" : 0, "AXRole" : "AXButton", "AXRows" : [ ], "AXSelected" : 0, "AXSelectedRows" : [ ], "AXSelectedText" : "", "AXSelectedTextRange" : " {value = location:0 length:0 type = kAXValueCFRangeType}", "AXSelectedTextRanges" : [ " {value = location:0 length:0 type = kAXValueCFRangeType}" ], "AXSubrole" : null, "AXTitle" : "", "AXTitleUIElement" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=913, title=\"Choose what to share with meet.google.com\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXURL" : null, "AXValue" : "", "AXVisibleCharacterRange" : " {value = location:0 length:0 type = kAXValueCFRangeType}", "AXVisited" : 0, "AXWindow" : "AXUIElement(AxWindowId=913, title=\"Choose what to share with meet.google.com\", role=\"AXWindow\", subrole=\"AXUnknown\")", "Aero.AxIgnored" : "AXFocused, AXRoleDescription, AXHelp, AXChildren, AXSize, AXFocusableAncestor, AXPosition, AXFrame", "ChromeAXNodeId" : "28846" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=913, title=\"Choose what to share with meet.google.com\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=441, title=\"Meet – hjq-dnoz-obo - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=552}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=913, title=\"Choose what to share with meet.google.com\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=441, title=\"Meet – hjq-dnoz-obo - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7151.104", "Aero.App.versionShort" : "137.0.7151.104", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 913, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/chrome_extensions_popup.json5 ================================================ // Top right corner -> Extensions { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1385.000000 y:124.000000 w:320.000000 h:537.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=34617}", "AXPosition" : " {value = x:1385.000000 y:124.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:320.000000 h:537.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=14076, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=34617}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=14076, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=14057, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7049.116", "Aero.App.versionShort" : "135.0.7049.116", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14076, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ { "commands" : "move-node-to-workspace F", "matcher" : "appId=\"com.google.Chrome\"" } ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/chrome_find_in_page.json5 ================================================ // cmd+f { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1131.000000 y:116.000000 w:403.000000 h:84.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=34617}", "AXPosition" : " {value = x:1131.000000 y:116.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:403.000000 h:84.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "Find in page\n New Tab", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14403, title=\"\", role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14403, title=\"Find in page\n New Tab\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14400, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=34617}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=14403, title=\"Find in page\n New Tab\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=14400, title=\"New Tab - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7049.116", "Aero.App.versionShort" : "135.0.7049.116", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14403, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ { "commands" : "move-node-to-workspace F", "matcher" : "appId=\"com.google.Chrome\"" } ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/chrome_pip.json5 ================================================ // Share you screen in GMeet { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1218.000000 y:587.000000 w:400.000000 h:225.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=34617}", "AXPosition" : " {value = x:1218.000000 y:587.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=34617}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:400.000000 h:225.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Picture-in-picture", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=14413, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14413, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=34617}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=14413, title=\"Picture-in-picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=14400, title=\"John Cena Prank Call - YouTube – Video playing in picture-in-picture mode - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7049.116", "Aero.App.versionShort" : "135.0.7049.116", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14413, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ { "commands" : "move-node-to-workspace F", "matcher" : "appId=\"com.google.Chrome\"" } ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/chrome_sharing_is_in_progress_popup.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:687.000000 y:1109.000000 w:426.000000 h:60.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=552}", "AXPosition" : " {value = x:687.000000 y:1109.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=552}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=552}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:426.000000 h:60.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "meet.google.com is sharing a window.", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=913, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=441, title=\"Meet – hjq-dnoz-obo - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=552}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Chrome", "AXWindows" : [ "AXUIElement(AxWindowId=920, title=\"meet.google.com is sharing a window.\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=913, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=441, title=\"Meet – hjq-dnoz-obo - Google Chrome\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.google.Chrome", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Google%20Chrome.app/Contents/MacOS/Google%20Chrome", "Aero.App.version" : "7151.104", "Aero.App.versionShort" : "137.0.7151.104", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 920, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 25, "Aero.workspace" : null } ================================================ FILE: axDumps/cleanshotx_monitor_1.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:2161.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:0.000000 w:3840.000000 h:2160.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=972}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:3840.000000 h:2160.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=972}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=972}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "CleanShot X", "AXWindows" : [ "AXUIElement(AxWindowId=70858, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70860, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70859, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "pl.maketheweb.cleanshotx", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///Applications/CleanShot%20X.app/", "Aero.App.nsApp.execPath" : "file:///Applications/CleanShot%20X.app/Contents/MacOS/CleanShot%20X", "Aero.App.pid" : 972, "Aero.App.version" : "4.8.4", "Aero.App.versionShort" : "4.8.4", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 70857, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ { "commands" : "layout floating", "matcher" : "appNameRegexSubstrin=Regex" } ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 103, "Aero.workspace" : null } ================================================ FILE: axDumps/cleanshotx_monitor_2.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:2161.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:2091.000000 w:694.000000 h:69.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=972}", "AXPosition" : " {value = x:0.000000 y:2091.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:694.000000 h:69.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=972}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=972}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "CleanShot X", "AXWindows" : [ "AXUIElement(AxWindowId=70858, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70857, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70860, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=70859, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "pl.maketheweb.cleanshotx", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///Applications/CleanShot%20X.app/", "Aero.App.nsApp.execPath" : "file:///Applications/CleanShot%20X.app/Contents/MacOS/CleanShot%20X", "Aero.App.pid" : 972, "Aero.App.version" : "4.8.4", "Aero.App.versionShort" : "4.8.4", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 70860, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ { "commands" : "layout floating", "matcher" : "appNameRegexSubstrin=Regex" } ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 103, "Aero.workspace" : null } ================================================ FILE: axDumps/drracket.json5 ================================================ { "AXActivationPoint" : " {value = x:70.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=52541}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=52541}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "main.rkt - DrRacket", "AXTitleUIElement" : "AXUIElement(AxWindowId=18886, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=52541}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "DrRacket", "AXWindows" : [ "AXUIElement(AxWindowId=19150, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=18886, title=\"main.rkt - DrRacket\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.racket-lang.DrRacket", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Racket%20v8.18/bin/../DrRacket.app/Contents/MacOS/DrRacket", "Aero.App.pid" : 52541, "Aero.App.version" : "8.18", "Aero.App.versionShort" : "8.18", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 18886, "Aero.macOS.version" : "Version 15.6 (Build 24G84)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/finder.json5 ================================================ { "AXActivationPoint" : " {value = x:10.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "FinderWindow", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1312}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : "AXUIElement(AxWindowId=14283, title=\"Macintosh HD\", role=\"AXImage\", subrole=nil)", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = \"Content Navigator\";\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXContentNavigator;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXSearch;\n}", "{\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXTopLevelNavigator;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "/Users/bobko", "AXTitleUIElement" : "AXUIElement(AxWindowId=14283, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXValue" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14283, title=nil, role=\"AXOutline\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : " {value = x:0.000000 y:1169.000000 w:0.000000 h:0.000000 type = kAXValueCGRectType}", "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1312}", "AXPosition" : " {value = x:0.000000 y:1169.000000 type = kAXValueCGPointType}", "AXRole" : "AXApplication", "AXSize" : " {value = w:0.000000 h:0.000000 type = kAXValueCGSizeType}", "AXTitle" : "Finder", "AXValue" : null, "AXWindows" : [ "AXUIElement(AxWindowId=14283, title=\"/Users/bobko\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", " {pid=1312}" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.finder", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Library/CoreServices/Finder.app/Contents/MacOS/Finder", "Aero.App.version" : "1732.4.3", "Aero.App.versionShort" : "15.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14283, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/finder_quick_look.json5 ================================================ // select any file -> press space { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXIdentifier" : "close panel button", "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : "", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXWindow" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1262.000000 y:316.000000 w:816.000000 h:849.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1312}", "AXPosition" : " {value = x:1262.000000 y:316.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1312}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:816.000000 h:849.000000 type = kAXValueCGSizeType}", "AXSubrole" : "Quick Look", "AXTitle" : "Quick Look", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXIdentifier" : "full screen window button", "AXLayoutDirection" : 0, "AXParent" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : "", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXWindow" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=3126, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXFrame" : " {value = x:0.000000 y:1169.000000 w:0.000000 h:0.000000 type = kAXValueCGRectType}", "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=15986, title=\"/opt/homebrew/Caskroom/aerospace/0.18.5-Beta/AeroSpace-v0.18.5-Beta/legal\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1312}", "AXPosition" : " {value = x:0.000000 y:1169.000000 type = kAXValueCGPointType}", "AXRole" : "AXApplication", "AXSize" : " {value = w:0.000000 h:0.000000 type = kAXValueCGSizeType}", "AXTitle" : "Finder", "AXValue" : null, "AXWindows" : [ "AXUIElement(AxWindowId=3126, title=\"Quick Look\", role=\"AXWindow\", subrole=\"Quick Look\")", "AXUIElement(AxWindowId=15986, title=\"/opt/homebrew/Caskroom/aerospace/0.18.5-Beta/AeroSpace-v0.18.5-Beta/legal\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", " {pid=1312}" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.finder", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Library/CoreServices/Finder.app/Contents/MacOS/Finder", "Aero.App.version" : "1732.4.3", "Aero.App.versionShort" : "15.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 3126, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/firefox.json5 ================================================ { "AXActivationPoint" : " {value = x:10.000000 y:59.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Mozilla Firefox", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13054, title=\"\", role=\"AXTextField\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=13054, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 13054, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "W" } ================================================ FILE: axDumps/firefox_extensions_popup.json5 ================================================ // Top right corner -> Extensions { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1257.000000 y:125.000000 w:379.000000 h:1045.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=15478}", "AXPosition" : " {value = x:1257.000000 y:125.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=15478}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=15478}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:379.000000 h:1045.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=1499, title=\"\", role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=1499, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=1499, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=15478}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=2198, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=1499, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.pid" : 15478, "Aero.App.version" : "14225.8.11", "Aero.App.versionShort" : "142.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections", "Aero.axWindowId" : 2198, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/firefox_mouse_hover_over_extensions_button.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1583.000000 y:125.000000 w:69.000000 h:18.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:1583.000000 y:125.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:69.000000 h:18.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "Extensions", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13054, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=13054, title=\"The small float tips in firefox will be trated as a window in v0.17.0-Beta · nikitabobko/AeroSpace · Discussion #1115\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13054, title=\"The small float tips in firefox will be trated as a window in v0.17.0-Beta · nikitabobko/AeroSpace · Discussion #1115\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=13196, title=\"Extensions\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13054, title=\"The small float tips in firefox will be trated as a window in v0.17.0-Beta · nikitabobko/AeroSpace · Discussion #1115\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 13196, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.windowLevel" : 101, "Aero.workspace" : null } ================================================ FILE: axDumps/firefox_mouse_hover_over_tab.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:569.000000 y:86.000000 w:280.000000 h:182.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:569.000000 y:86.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:280.000000 h:182.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13054, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=13054, title=\"Chrome extensions popup is mistakenly being detected a window. 0.18.0 regression · Issue #1324 · nikitabobko/AeroSpace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13054, title=\"Chrome extensions popup is mistakenly being detected a window. 0.18.0 regression · Issue #1324 · nikitabobko/AeroSpace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=12431, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13054, title=\"Chrome extensions popup is mistakenly being detected a window. 0.18.0 regression · Issue #1324 · nikitabobko/AeroSpace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 12431, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/firefox_non_native_fullscreen.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:0.000000 w:1800.000000 h:1169.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1169.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "Поперечный - ПОП КУЛЬТУРА - YouTube", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14384, title=\"YouTube Video Player in Full screen\", role=\"AXGroup\", subrole=\"AXApplicationGroup\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14384, title=\"Поперечный - ПОП КУЛЬТУРА - YouTube\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14384, title=\"Поперечный - ПОП КУЛЬТУРА - YouTube\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=14384, title=\"Поперечный - ПОП КУЛЬТУРА - YouTube\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13054, title=\"YouTube\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14384, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "W" } ================================================ FILE: axDumps/firefox_normal_window_when_non_native_fullscreen_in_background.json5 ================================================ { "AXActivationPoint" : " {value = x:10.000000 y:59.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, // Honestly, I think it's a firefox bug "AXParent" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=24011}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=24011}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Mozilla Firefox", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4294, title=\"\", role=\"AXTextField\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=24011}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=4294, title=\"Mozilla Firefox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.5.29", "Aero.App.versionShort" : "139.0.1", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 4294, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, // todo fix "Aero.AxUiElementWindowType" : "dialog", "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "W" } ================================================ FILE: axDumps/firefox_pinterest_sign_in_with_google.json5 ================================================ // https://www.pinterest.com -> Sign in with Google { "AXActivationPoint" : " {value = x:1869.000000 y:1155.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1799.000000 y:1141.000000 w:500.000000 h:618.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:1799.000000 y:1141.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:500.000000 h:618.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Inloggen - Google Accounts", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14685, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=14685, title=\"Inloggen - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13054, title=\"Pinterest\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14685, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, // todo fix? "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "W" } ================================================ FILE: axDumps/firefox_pip.json5 ================================================ { "AXActivationPoint" : " {value = x:1057.000000 y:423.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1047.000000 y:409.000000 w:715.000000 h:402.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=1405}", "AXPosition" : " {value = x:1047.000000 y:409.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=1405}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:715.000000 h:402.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Picture-in-Picture", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXGroup\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=1405}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=14336, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13839, title=\"Поперечный - ПОП КУЛЬТУРА - YouTube\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13054, title=\"YouTube\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13725.4.14", "Aero.App.versionShort" : "137.0.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14336, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/ghostty.json5 ================================================ // ~/.config/ghostty/config -> window-decoration = true (default) { "AXActivationPoint" : " {value = x:100.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/", "AXFocused" : 0, "AXFrame" : " {value = x:30.000000 y:44.000000 w:1770.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "TerminalWindowRestoration", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=41303}", "AXPosition" : " {value = x:30.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : "AXUIElement(AxWindowId=14591, title=\"bobko\", role=\"AXImage\", subrole=nil)", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=41303}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=41303}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1770.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "~", "AXTitleUIElement" : "AXUIElement(AxWindowId=14591, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14591, title=nil, role=\"AXUnknown\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=41303}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=14591, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.version" : "9438", "Aero.App.versionShort" : "1.1.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14591, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/ghostty_about.json5 ================================================ // Menu bar -> Ghostty -> About Ghostty { "AXActivationPoint" : " {value = x:970.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:300.000000 h:429.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:9", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=70565}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=70565}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:300.000000 h:429.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=21795, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=21795, title=nil, role=\"AXLink\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=70565}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=21795, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=21704, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.version" : "9438", "Aero.App.versionShort" : "1.1.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 21795, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.macOS.version" : "Version 15.4.1 (Build 24E263)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "G" } ================================================ FILE: axDumps/ghostty_check_for_updates_1_dialog.json5 ================================================ // Menu bar -> Ghostty -> Check for Updates... { "AXActivationPoint" : " {value = x:710.000000 y:306.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:700.000000 y:292.000000 w:400.000000 h:134.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "SUStatus", "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=70565}", "AXPosition" : " {value = x:700.000000 y:292.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:400.000000 h:134.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Updating Ghostty", "AXTitleUIElement" : "AXUIElement(AxWindowId=21898, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:8", "AXParent" : "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Cancel", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=70565}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=21898, title=\"Updating Ghostty\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=21704, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.version" : "9438", "Aero.App.versionShort" : "1.1.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 21898, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, // todo fix "Aero.AxUiElementWindowType" : "window", "Aero.macOS.version" : "Version 15.4.1 (Build 24E263)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "G" } ================================================ FILE: axDumps/ghostty_check_for_updates_2_alert.json5 ================================================ { "AXActivationPoint" : " {value = x:780.000000 y:276.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : { "AXEnabled" : 1, "AXIdentifier" : "action-button-1", "AXParent" : "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXTitle" : "OK", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXDescription" : "alert", "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:770.000000 y:260.000000 w:260.000000 h:252.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:87", "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 1, "AXParent" : " {pid=18587}", "AXPosition" : " {value = x:770.000000 y:260.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=18587}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:260.000000 h:252.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2854, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=18587}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=2866, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=2854, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Ghostty.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.pid" : 18587, "Aero.App.version" : "12214", "Aero.App.versionShort" : "1.2.3", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXTitleUIElement, get.AXCancelButton, isWritable.AXDescription, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections", "Aero.axWindowId" : 2866, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 8, "Aero.workspace" : "M" } ================================================ FILE: axDumps/ghostty_config_error.json5 ================================================ { "AXActivationPoint" : " {value = x:470.000000 y:180.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:400.000000 y:166.000000 w:960.000000 h:682.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:11", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=70565}", "AXPosition" : " {value = x:400.000000 y:166.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=70565}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:960.000000 h:682.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Configuration Errors", "AXTitleUIElement" : "AXUIElement(AxWindowId=21850, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=21850, title=nil, role=\"AXStaticText\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=70565}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=21850, title=\"Configuration Errors\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=21704, title=\"ta ~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.version" : "9438", "Aero.App.versionShort" : "1.1.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 21850, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.macOS.version" : "Version 15.4.1 (Build 24E263)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 101, "Aero.workspace" : "G" } ================================================ FILE: axDumps/ghostty_quick_terminal.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:-172.000000 w:1800.000000 h:400.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "com.mitchellh.ghostty.quickTerminal", "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=18587}", "AXPosition" : " {value = x:0.000000 y:-172.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=18587}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=18587}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:400.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXFloatingWindow", "AXTitle" : "~", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2861, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=2861, title=\"~\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2861, title=\"~\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXMenuBar" : " {pid=18587}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=2861, title=\"~\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXUIElement(AxWindowId=2854, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Ghostty.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.pid" : 18587, "Aero.App.version" : "12214", "Aero.App.versionShort" : "1.2.3", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2861, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/ghostty_window_decorations_false.json5 ================================================ // ~/.config/ghostty/config -> window-decoration = false { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/", "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "TerminalWindowRestoration", "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=46546}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=46546}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=46546}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "~", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14769, title=nil, role=\"AXUnknown\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14769, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14769, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=46546}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Ghostty", "AXWindows" : [ "AXUIElement(AxWindowId=14769, title=\"~\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.mitchellh.ghostty", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Ghostty.app/Contents/MacOS/ghostty", "Aero.App.version" : "9438", "Aero.App.versionShort" : "1.1.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14769, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/intellij.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:30.000000 y:44.000000 w:1770.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:30.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1770.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13585, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 13585, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "I" } ================================================ FILE: axDumps/intellij_background_tasks.json5 ================================================ // run some long running action (e.g. "rebuild project", reimport project model, etc.) -> bottom right corner -> click -> "Background Tasks" popup { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1338.000000 y:999.000000 w:442.000000 h:134.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:1338.000000 y:999.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:442.000000 h:134.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : " {pid=8947}", "AXFocusedWindow" : "AXUIElement(AxWindowId=14780, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/test/kotlin/jvm/gradle/sandbox/AppTest.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=14780, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/test/kotlin/jvm/gradle/sandbox/AppTest.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14780, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.workspace" : null } ================================================ FILE: axDumps/intellij_context_menu.json5 ================================================ // Right click in the editor { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:712.000000 y:402.000000 w:227.000000 h:468.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:712.000000 y:402.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:227.000000 h:468.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13585, title=nil, role=\"AXGroup\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=14241, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14241, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.workspace" : null } ================================================ FILE: axDumps/intellij_native_open_window.json5 ================================================ // File -> Open... { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:311.000000 y:45.000000 w:1177.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "open-panel", "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 1, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:311.000000 y:45.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:1177.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Open File or Project", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14487, title=nil, role=\"AXList\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14486, title=\"Open File or Project\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14486, title=\"Open File or Project\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=14486, title=\"Open File or Project\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14486, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.workspace" : "I" } ================================================ FILE: axDumps/intellij_quick_doc_popup.json5 ================================================ // Shift + K (IdeaVim) on any symbol { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:576.000000 y:187.000000 w:314.000000 h:113.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:576.000000 y:187.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:314.000000 h:113.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14242, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14242, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=14242, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/main/kotlin/jvm/gradle/sandbox/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14242, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.workspace" : null } ================================================ FILE: axDumps/intellij_rebase_dialog.json5 ================================================ // Find Action... -> Rebase { "AXActivationPoint" : " {value = x:745.000000 y:557.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:675.000000 y:543.000000 w:479.000000 h:125.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=8947}", "AXPosition" : " {value = x:675.000000 y:543.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=8947}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:479.000000 h:125.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Rebase", "AXTitleUIElement" : "AXUIElement(AxWindowId=14799, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14799, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=8947}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "IntelliJ IDEA", "AXWindows" : [ "AXUIElement(AxWindowId=14799, title=\"Rebase\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=13585, title=\"jvm-gradle-sandbox [~/a/jvm-gradle-sandbox] – /Users/bobko/a/jvm-gradle-sandbox/app/src/test/kotlin/jvm/gradle/sandbox/AppTest.kt\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=13583, title=\"jvm-sandbox [~/a/jvm-sandbox] – /Users/bobko/a/jvm-sandbox/src/main/kotlin/main.kt\", role=\"AXWindow\", subrole=\"AXDialog\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.intellij", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Applications/ij-241.app/Contents/MacOS/idea", "Aero.App.version" : "IU-241.17011.79", "Aero.App.versionShort" : "2024.1.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14799, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.workspace" : "I" } ================================================ FILE: axDumps/iphonesimulator.json5 ================================================ { "AXActivationPoint" : " {value = x:1809.000000 y:1131.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1799.000000 y:1117.000000 w:447.000000 h:950.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=78264}", "AXPosition" : " {value = x:1799.000000 y:1117.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=78264}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=78264}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:447.000000 h:950.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "iPhone 15 Pro – iOS 17.4", "AXTitleUIElement" : "AXUIElement(AxWindowId=9515, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Action", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=78264}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Simulator", "AXWindows" : [ "AXUIElement(AxWindowId=9515, title=\"iPhone 15 Pro – iOS 17.4\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.iphonesimulator", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator", "Aero.App.version" : "1042.1", "Aero.App.versionShort" : "16.0", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 9515, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "X" } ================================================ FILE: axDumps/iterm2.json5 ================================================ { "AXActivationPoint" : " {value = x:30.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:40.000000 w:1800.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=20059}", "AXPosition" : " {value = x:0.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=20059}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "aerospace", "AXTitleUIElement" : "AXUIElement(AxWindowId=2888, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2888, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=20059}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "iTerm2", "AXWindows" : [ "AXUIElement(AxWindowId=2888, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.googlecode.iterm2", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/iTerm.app/", "Aero.App.nsApp.execPath" : "file:///Applications/iTerm.app/Contents/MacOS/iTerm2", "Aero.App.pid" : 20059, "Aero.App.version" : "3.6.5", "Aero.App.versionShort" : "3.6.5", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2888, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/iterm2_hotkey_window.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:40.000000 w:1800.000000 h:667.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23805}", "AXPosition" : " {value = x:0.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23805}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:667.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "aerospace", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=3017, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23805}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "iTerm2", "AXWindows" : [ "AXUIElement(AxWindowId=3017, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.googlecode.iterm2", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/iTerm.app/", "Aero.App.nsApp.execPath" : "file:///Applications/iTerm.app/Contents/MacOS/iTerm2", "Aero.App.pid" : 23805, "Aero.App.version" : "3.6.5", "Aero.App.versionShort" : "3.6.5", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 3017, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/iterm2_no_title_bar.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:30.000000 y:40.000000 w:1740.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3026, title=\"-zsh\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23805}", "AXPosition" : " {value = x:30.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23805}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1740.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "-zsh", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=3026, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23805}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "iTerm2", "AXWindows" : [ "AXUIElement(AxWindowId=3026, title=\"aerospace\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.googlecode.iterm2", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/iTerm.app/", "Aero.App.nsApp.execPath" : "file:///Applications/iTerm.app/Contents/MacOS/iTerm2", "Aero.App.pid" : 23805, "Aero.App.version" : "3.6.5", "Aero.App.versionShort" : "3.6.5", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 3026, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "1" } ================================================ FILE: axDumps/jetbrains_toolbox.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1032.000000 y:48.000000 w:440.000000 h:700.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=92331}", "AXPosition" : " {value = x:1032.000000 y:48.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=92331}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=92331}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:440.000000 h:700.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Toolbox", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=92331}", "AXFocusedUIElement" : " {pid=92331}", "AXFocusedWindow" : "AXUIElement(AxWindowId=14148, title=\"Toolbox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14148, title=\"Toolbox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : null, "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "JetBrains Toolbox", "AXWindows" : [ "AXUIElement(AxWindowId=14148, title=\"Toolbox\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.jetbrains.toolbox", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/JetBrains%20Toolbox.app/Contents/MacOS/jetbrains-toolbox", "Aero.App.version" : "2.6.0.40632", "Aero.App.versionShort" : "2.6.0.40632", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14148, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.workspace" : "M" } ================================================ FILE: axDumps/karabiner_event_viewer.json5 ================================================ { "AXActivationPoint" : " {value = x:670.000000 y:54.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:600.000000 y:40.000000 w:1100.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=94075}", "AXPosition" : " {value = x:600.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=94075}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=94075}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=94075}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1100.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Karabiner-EventViewer", "AXTitleUIElement" : "AXUIElement(AxWindowId=4769, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4769, title=nil, role=\"AXGroup\", subrole=\"AXHostingView\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=94075}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Karabiner-EventViewer", "AXWindows" : [ "AXUIElement(AxWindowId=4769, title=\"Karabiner-EventViewer\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.pqrs.Karabiner-EventViewer", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Karabiner-EventViewer.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Karabiner-EventViewer.app/Contents/MacOS/Karabiner-EventViewer", "Aero.App.pid" : 94075, "Aero.App.version" : "15.2.0", "Aero.App.versionShort" : "15.2.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4769, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/karabiner_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:970.000000 y:54.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:40.000000 w:1100.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=94014}", "AXPosition" : " {value = x:900.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=94014}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = \"Content Navigator\";\n SectionObject = \" {pid=94014}\";\n SectionUniqueID = AXContentNavigator;\n}" ], "AXSize" : " {value = w:1100.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Karabiner-Elements Settings", "AXTitleUIElement" : "AXUIElement(AxWindowId=4758, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : { "AXActivationPoint" : " {value = x:1025.000000 y:88.000000 type = kAXValueCGPointType}", "AXAttributedDescription" : "Simple Modifications{\n AXATextAlignmentValue = 2;\n AXFont = {\n AXFontFamily = \".AppleSystemUIFont\";\n AXFontName = \".SFNS-Regular\";\n AXFontSize = 13;\n AXVisibleName = \"System Font Regular\";\n };\n AXForegroundColor = \" [ (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Generic RGB Profile)] headroom = 1.000000 ( 1 1 1 1 )\";\n}", "AXAutoInteractable" : 0, "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4758, title=nil, role=\"AXGroup\", subrole=\"AXHostingView\")", "AXPath" : "Path 0x979038c80:\n moveto (0, 1141)\n lineto (250, 1141)\n lineto (250, 1101)\n lineto (0, 1101)\n closepath\n moveto (0, 1141)\n", "AXRole" : "AXButton", "AXSubrole" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXSubrole", "Aero.AxIgnored" : "AXFocused, AXRoleDescription, AXChildren, AXChildrenInNavigationOrder, AXFrame, AXSize, AXPosition" }, "AXFocusedWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=94014}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Karabiner-Elements", "AXWindows" : [ "AXUIElement(AxWindowId=4758, title=\"Karabiner-Elements Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.pqrs.Karabiner-Elements.Settings", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Karabiner-Elements.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Karabiner-Elements.app/Contents/MacOS/Karabiner-Elements", "Aero.App.pid" : 94014, "Aero.App.version" : "15.2.0", "Aero.App.versionShort" : "15.2.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4758, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/kitty_quick_access.json5 ================================================ // /opt/homebrew/bin/kitten quick-access-terminal -- /opt/homebrew/bin/spacelist { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:42.000000 w:1800.000000 h:363.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=19245}", "AXPosition" : " {value = x:0.000000 y:42.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=19245}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:363.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "kitty", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=66133, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=66133, title=\"kitty\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=66133, title=\"kitty\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXMenuBar" : " {pid=19245}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "kitty-quick-access", "AXWindows" : [ "AXUIElement(AxWindowId=66133, title=\"kitty\", role=\"AXWindow\", subrole=\"AXUnknown\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "net.kovidgoyal.kitty-quick-access", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///Applications/kitty.app/Contents/kitty-quick-access.app/", "Aero.App.nsApp.execPath" : "file:///Applications/kitty.app/Contents/kitty-quick-access.app/Contents/MacOS/kitty-quick-access", "Aero.App.pid" : 19245, "Aero.App.version" : "0.42.2", "Aero.App.versionShort" : "0.42.2", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 66133, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 100, "Aero.workspace" : null } ================================================ FILE: axDumps/macos_capslock_popup_safari.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1233.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:323.000000 y:41.000000 w:83.000000 h:77.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=656}", "AXPosition" : " {value = x:323.000000 y:41.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:83.000000 h:77.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=50, title=nil, role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=50, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXIsScribbleActive" : 0, "AXMainWindow" : "AXUIElement(AxWindowId=50, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=656}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Safari", "AXWindows" : [ "AXUIElement(AxWindowId=49, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=50, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.Safari", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/MacOS/Safari", "Aero.App.version" : "21622.1.14.11.4", "Aero.App.versionShort" : "19.0", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 49, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.macOS.version" : "Version 26.0 Beta (Build 25A5279m)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/macos_capslock_popup_textedit.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1233.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:-6.000000 y:128.000000 w:83.000000 h:77.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=928}", "AXPosition" : " {value = x:-6.000000 y:128.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:83.000000 h:77.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=224, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=224, title=\"Untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=224, title=\"Untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=928}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "TextEdit", "AXWindows" : [ "AXUIElement(AxWindowId=223, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=224, title=\"Untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.TextEdit", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Applications/TextEdit.app/Contents/MacOS/TextEdit", "Aero.App.version" : "412", "Aero.App.versionShort" : "1.20", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 223, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.macOS.version" : "Version 26.0 Beta (Build 25A5279m)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/macos_join_network.json5 ================================================ { "AXActivationPoint" : " {value = x:470.000000 y:220.000000 type = kAXValueCGPointType}", "AXCancelButton" : { "AXEnabled" : 1, "AXIdentifier" : "_NS:30", "AXParent" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXTitle" : "Cancel", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:400.000000 y:206.000000 w:900.000000 h:578.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:96", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=73503}", "AXPosition" : " {value = x:400.000000 y:206.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=73503}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:900.000000 h:578.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Join Network", "AXTitleUIElement" : "AXUIElement(AxWindowId=9780, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : " {pid=73508}", "AXFocusedWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : null, "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Captive Network Assistant", "AXWindows" : [ "AXUIElement(AxWindowId=9780, title=\"Join Network\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.CaptiveNetworkAssistant", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///System/Library/CoreServices/Captive%20Network%20Assistant.app/", "Aero.App.nsApp.execPath" : "file:///System/Library/CoreServices/Captive%20Network%20Assistant.app/Contents/MacOS/Captive%20Network%20Assistant", "Aero.App.pid" : 73503, "Aero.App.version" : "5.0", "Aero.App.versionShort" : "5.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 9780, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 8, "Aero.workspace" : "1" } ================================================ FILE: axDumps/macos_share_window_purple_pill_sublime.json5 ================================================ // Share purple "pill" indicator in the window's top left corner - https://github.com/nikitabobko/AeroSpace/issues/1101 { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:908.000000 y:48.000000 w:52.000000 h:20.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:8", "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=555}", "AXPosition" : " {value = x:908.000000 y:48.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:52.000000 h:20.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Window", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=65, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=65, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=65, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=555}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Sublime Text", "AXWindows" : [ "AXUIElement(AxWindowId=922, title=\"Window\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=65, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.sublimetext.4", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Sublime%20Text.app/Contents/MacOS/sublime_text", "Aero.App.version" : "4200", "Aero.App.versionShort" : "Build 4200", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 922, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/marta.json5 ================================================ { "AXActivationPoint" : " {value = x:970.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=41210}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=41210}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=41210}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=41210}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=41210}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Marta", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14545, title=nil, role=\"AXScrollArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=41210}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Marta", "AXWindows" : [ "AXUIElement(AxWindowId=14545, title=\"Marta\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.yanex.marta", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Marta.app/Contents/MacOS/Marta", "Aero.App.version" : "0.8.200", "Aero.App.versionShort" : "0.8.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14545, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/microsoft_edge.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "https://www.youtube.com/watch?v=N0WuXG1wGQk", "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:40.000000 w:900.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=58546}", "AXPosition" : " {value = x:900.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=58546}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=58546}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=58546}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Edge", "AXWindows" : [ "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.microsoft.edgemac", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Microsoft%20Edge.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Microsoft%20Edge.app/Contents/MacOS/Microsoft%20Edge", "Aero.App.pid" : 58546, "Aero.App.version" : "142.3595.25111994", "Aero.App.versionShort" : "142.0.3595.94", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4350, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/microsoft_edge_pip.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1175.000000 y:807.000000 w:602.000000 h:339.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=58546}", "AXPosition" : " {value = x:1175.000000 y:807.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=58546}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=58546}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:602.000000 h:339.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Picture in Picture", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=58546}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Edge", "AXWindows" : [ "AXUIElement(AxWindowId=4363, title=\"Picture in Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=4350, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube - Video playing in picture-in-picture mode - Microsoft Edge\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.microsoft.edgemac", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Microsoft%20Edge.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Microsoft%20Edge.app/Contents/MacOS/Microsoft%20Edge", "Aero.App.pid" : 58546, "Aero.App.version" : "142.3595.25111994", "Aero.App.versionShort" : "142.0.3595.94", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXSize", "Aero.axWindowId" : 4363, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/mpv_fullscreen.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:0.000000 w:1800.000000 h:1169.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=38857}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:1800.000000 h:1169.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXMenuBar" : " {pid=38857}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "mpv", "AXWindows" : [ "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXUnknown\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "NULL-APP-BUNDLE-ID", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///opt/homebrew/bin/mpv", "Aero.App.version" : null, "Aero.App.versionShort" : null, "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14286, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", // todo fix? "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.workspace" : null } ================================================ FILE: axDumps/mpv_windowed.json5 ================================================ { "AXActivationPoint" : " {value = x:1210.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:1200.000000 y:44.000000 w:599.000000 h:337.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=38857}", "AXPosition" : " {value = x:1200.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=38857}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:599.000000 h:337.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv", "AXTitleUIElement" : "AXUIElement(AxWindowId=14286, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=38857}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "mpv", "AXWindows" : [ "AXUIElement(AxWindowId=14286, title=\"The past, present, and future of local-first - Martin Kleppmann (Local-First Conf) [NMq0vncHJvU].mkv - mpv\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "NULL-APP-BUNDLE-ID", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///opt/homebrew/bin/mpv", "Aero.App.version" : null, "Aero.App.versionShort" : null, "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14286, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.workspace" : "M" } ================================================ FILE: axDumps/nomachine_session_1.json5 ================================================ { "AXActivationPoint" : " {value = x:10.000000 y:70.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:56.000000 w:1512.000000 h:922.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=98872}", "AXPosition" : " {value = x:0.000000 y:56.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=98872}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1512.000000 h:922.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=38736, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=38736, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=38736, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=38736, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=98872}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "NoMachine", "AXWindows" : [ "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=38736, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.nomachine.nxdock", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/NoMachine.app/Contents/MacOS/nxplayer", "Aero.App.version" : "8.17.2", "Aero.App.versionShort" : "8.17.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 38736, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ { "commands" : "layout floating; move-node-to-workspace V", "matcher" : "appId=\"com.nomachine.nxdock\"" } ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "C" } ================================================ FILE: axDumps/nomachine_session_2.json5 ================================================ { "AXActivationPoint" : " {value = x:1990.000000 y:264.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1920.000000 y:250.000000 w:1512.000000 h:950.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=98872}", "AXPosition" : " {value = x:1920.000000 y:250.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=98872}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1512.000000 h:950.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "NoMachine - ubuntu20, Ubuntu 22.04.5 LTS", "AXTitleUIElement" : "AXUIElement(AxWindowId=38734, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=38734, title=nil, role=\"AXUnknown\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=98872}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "NoMachine", "AXWindows" : [ "AXUIElement(AxWindowId=38734, title=\"NoMachine - ubuntu20, Ubuntu 22.04.5 LTS\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=38736, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.nomachine.nxdock", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/NoMachine.app/Contents/MacOS/nxplayer", "Aero.App.version" : "8.17.2", "Aero.App.versionShort" : "8.17.2", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 38734, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ { "commands" : "layout floating; move-node-to-workspace V", "matcher" : "appId=\"com.nomachine.nxdock\"" } ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "V" } ================================================ FILE: axDumps/nomachine_welcome_window_1.json5 ================================================ // NoMachine is weird. It shows one visible window, but creates 2 AX objects. // AeroSpace has to deliberatery ignore the redundant AX object to avoid weird focus issues // ./nomachine_welcome_window_1.json5 -- valid AX object // ./nomachine_welcome_window_2.json5 -- weird AX object { "AXActivationPoint" : " {value = x:930.000000 y:663.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:920.000000 y:649.000000 w:880.000000 h:520.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=64949}", "AXPosition" : " {value = x:920.000000 y:649.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=64949}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:880.000000 h:520.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=9384, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=9384, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=9384, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 0, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=9384, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=64949}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "NoMachine", "AXWindows" : [ "AXUIElement(AxWindowId=9384, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.nomachine.nxdock", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/NoMachine.app/Contents/MacOS/nxplayer", "Aero.App.pid" : 64949, "Aero.App.version" : "8.16.1", "Aero.App.versionShort" : "8.16.1", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 9384, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "M" } ================================================ FILE: axDumps/nomachine_welcome_window_2.json5 ================================================ // NoMachine is weird. It shows one visible window, but creates 2 AX objects. // AeroSpace has to deliberatery ignore the redundant AX object to avoid weird focus issues // ./nomachine_welcome_window_1.json5 -- valid AX object // ./nomachine_welcome_window_2.json5 -- weird AX object { "AXActivationPoint" : " {value = x:603.000000 y:343.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:533.000000 y:329.000000 w:880.000000 h:548.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=64949}", "AXPosition" : " {value = x:533.000000 y:329.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=64949}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:880.000000 h:548.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "NoMachine", "AXTitleUIElement" : "AXUIElement(AxWindowId=9383, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=9383, title=nil, role=\"AXUnknown\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=64949}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "NoMachine", "AXWindows" : [ "AXUIElement(AxWindowId=9383, title=\"NoMachine\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.nomachine.nxdock", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/NoMachine.app/Contents/MacOS/nxplayer", "Aero.App.pid" : 64949, "Aero.App.version" : "8.16.1", "Aero.App.versionShort" : "8.16.1", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 9383, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "M" } ================================================ FILE: axDumps/qutebrowser.json5 ================================================ { "AXActivationPoint" : " {value = x:970.000000 y:54.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:40.000000 w:900.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=96882}", "AXPosition" : " {value = x:900.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=96882}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = \"Top Level Navigator\";\n SectionObject = \" {pid=96882}\";\n SectionUniqueID = AXTopLevelNavigator;\n}", "{\n SectionObject = \" {pid=96882}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Change Log - qutebrowser", "AXTitleUIElement" : "AXUIElement(AxWindowId=4815, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4815, title=\"\", role=\"AXGroup\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=96882}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "qutebrowser", "AXWindows" : [ "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.qutebrowser.qutebrowser", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/qutebrowser.app/", "Aero.App.nsApp.execPath" : "file:///Applications/qutebrowser.app/Contents/MacOS/qutebrowser", "Aero.App.pid" : 96882, "Aero.App.version" : "3.6.1", "Aero.App.versionShort" : "3.6.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4815, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/qutebrowser_context_menu.json5 ================================================ // Right click { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1358.000000 y:160.000000 w:164.000000 h:122.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=96882}", "AXPosition" : " {value = x:1358.000000 y:160.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=96882}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:164.000000 h:122.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4815, title=\"Change Log\", role=\"AXGroup\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=96882}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "qutebrowser", "AXWindows" : [ "AXUIElement(AxWindowId=4820, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=4815, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.qutebrowser.qutebrowser", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/qutebrowser.app/", "Aero.App.nsApp.execPath" : "file:///Applications/qutebrowser.app/Contents/MacOS/qutebrowser", "Aero.App.pid" : 96882, "Aero.App.version" : "3.6.1", "Aero.App.versionShort" : "3.6.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXPosition, AXSections, AXMinimized", "Aero.axWindowId" : 4820, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 101, "Aero.workspace" : null } ================================================ FILE: axDumps/qutebrowser_hide_decoration.json5 ================================================ // :set window.hide_decoration { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:40.000000 w:900.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=96739}", "AXPosition" : " {value = x:900.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=96739}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = \"Top Level Navigator\";\n SectionObject = \" {pid=96739}\";\n SectionUniqueID = AXTopLevelNavigator;\n}", "{\n SectionObject = \" {pid=96739}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Change Log - qutebrowser", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4799, title=\"Change Log\", role=\"AXGroup\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4799, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : null, "AXMenuBar" : " {pid=96739}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "qutebrowser", "AXWindows" : [ "AXUIElement(AxWindowId=4799, title=\"Change Log - qutebrowser\", role=\"AXWindow\", subrole=\"AXDialog\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXMainWindow, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.qutebrowser.qutebrowser", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/qutebrowser.app/", "Aero.App.nsApp.execPath" : "file:///Applications/qutebrowser.app/Contents/MacOS/qutebrowser", "Aero.App.pid" : 96739, "Aero.App.version" : "3.6.1", "Aero.App.versionShort" : "3.6.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXPosition, AXSections, AXMinimized, AXSize", "Aero.axWindowId" : 4799, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/raycast.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:525.000000 y:188.000000 w:750.000000 h:474.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=76823}", "AXPosition" : " {value = x:525.000000 y:188.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=76823}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:750.000000 h:474.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXSystemDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=26499, title=nil, role=\"AXTextField\", subrole=\"raycast_searchField\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=26499, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=26499, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXMenuBar" : " {pid=76823}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Raycast", "AXWindows" : [ "AXUIElement(AxWindowId=26499, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.raycast.macos", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Raycast.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Raycast.app/Contents/MacOS/Raycast", "Aero.App.pid" : 76823, "Aero.App.version" : "0", "Aero.App.versionShort" : "1.103.7", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 26499, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 8, "Aero.workspace" : null } ================================================ FILE: axDumps/raycast_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:470.000000 y:167.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:400.000000 y:153.000000 w:1000.000000 h:604.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=76823}", "AXPosition" : " {value = x:400.000000 y:153.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=76823}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1000.000000 h:604.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Settings", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=76823}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Raycast", "AXWindows" : [ "AXUIElement(AxWindowId=28386, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.raycast.macos", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Raycast.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Raycast.app/Contents/MacOS/Raycast", "Aero.App.pid" : 76823, "Aero.App.version" : "0", "Aero.App.versionShort" : "1.103.7", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 28386, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/safari.json5 ================================================ { "AXActivationPoint" : " {value = x:910.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "SafariWindow?IsSecure=false&UUID=DC64557C-318F-4644-8AE3-8925469E902C", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=45941}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=45941}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionDescription = Content;\n SectionObject = \" {pid=45941}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = \"Top Level Navigator\";\n SectionObject = \" {pid=45941}\";\n SectionUniqueID = AXTopLevelNavigator;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Start Page", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14665, title=nil, role=\"AXTextField\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXIsScribbleActive" : 0, "AXMainWindow" : "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=45941}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Safari", "AXWindows" : [ "AXUIElement(AxWindowId=14665, title=\"Start Page\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.Safari", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/MacOS/Safari", "Aero.App.version" : "20621.1.15.11.10", "Aero.App.versionShort" : "18.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14665, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/safari_pinterest_sign_in_with_google.json5 ================================================ // https://www.pinterest.com -> Sign in with Google { "AXActivationPoint" : " {value = x:720.000000 y:323.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:650.000000 y:309.000000 w:500.000000 h:550.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "SafariWindow?IsSecure=true&UUID=5741B7F9-21AF-426C-B8AA-6DE263C3792B", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=16736}", "AXPosition" : " {value = x:650.000000 y:309.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=16736}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = \"Top Level Navigator\";\n SectionObject = \" {pid=16736}\";\n SectionUniqueID = AXTopLevelNavigator;\n}" ], "AXSize" : " {value = w:500.000000 h:550.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Sign in - Google Accounts", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : " {pid=16759}", "AXFocusedWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXIsScribbleActive" : 0, "AXMainWindow" : "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=16736}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Safari", "AXWindows" : [ "AXUIElement(AxWindowId=2210, title=\"Sign in - Google Accounts\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=2204, title=\"Pinterest\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, isWritable.AXIsScribbleActive, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.Safari", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Volumes/Preboot/Cryptexes/App/System/Applications/Safari.app/Contents/MacOS/Safari", "Aero.App.pid" : 16736, "Aero.App.version" : "20621.3.11.11.3", "Aero.App.versionShort" : "18.6", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2210, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/01_firefox.json5 ================================================ { "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=310, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 11, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "F" } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/02_firefox.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:298.000000 y:123.000000 w:405.000000 h:167.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=979}", "AXPosition" : " {value = x:298.000000 y:123.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:405.000000 h:167.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=310, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 12, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/03_firefox.json5 ================================================ { "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=318, title=\"Firefox — Sharing Indicator\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 13, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "F" } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/04_firefox.json5 ================================================ { "AXActivationPoint" : " {value = x:736.000000 y:150.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:710.000000 y:129.000000 w:379.000000 h:32.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=979}", "AXPosition" : " {value = x:710.000000 y:129.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=979}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=979}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:379.000000 h:32.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Firefox — Sharing Indicator", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=318, title=\"Firefox — Sharing Indicator\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 14, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/05_apple_controlcenter.json5 ================================================ { "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=562}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=385, title=nil, role=\"AXGroup\", subrole=\"AXHostingView\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=385, title=nil, role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : null, "AXMenuBar" : null, "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Control Centre", "AXWindows" : [ ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.controlcenter", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///System/Library/CoreServices/ControlCenter.app/Contents/MacOS/ControlCenter", "Aero.App.version" : "1", "Aero.App.versionShort" : "1.0", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 15, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "F" } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/06_firefox.json5 ================================================ { "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=318, title=\"Firefox — Sharing Indicator\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=391, title=\"Window\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 17, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "F" } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/07_firefox.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:13.000000 y:56.000000 w:52.000000 h:20.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:8", "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=979}", "AXPosition" : " {value = x:13.000000 y:56.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : null, "AXSize" : " {value = w:52.000000 h:20.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Window", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=979}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=307, title=nil, role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=979}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Firefox", "AXWindows" : [ "AXUIElement(AxWindowId=318, title=\"Firefox — Sharing Indicator\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=391, title=\"Window\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=307, title=\"Meet – rsf-trih-oxo\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.mozilla.firefox", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Firefox.app/Contents/MacOS/firefox", "Aero.App.version" : "13925.6.9", "Aero.App.versionShort" : "139.0.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 18, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/scenario_firefox_google_meet_share_window/README.md ================================================ 1. Open Firefox 2. https://meet.google.com/ 3. Create an instant meeting 4. Select the current Firefox window During these steps the user encounters 7 (!) different small popups. Including share purple "pill" indicator in the window's top left corner - https://github.com/nikitabobko/AeroSpace/issues/1101 ================================================ FILE: axDumps/slack.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=80239}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=80239}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=80239}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "general (Channel) - redacted - Slack", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=80239}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Slack", "AXWindows" : [ "AXUIElement(AxWindowId=5018, title=\"general (Channel) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.tinyspeck.slackmacgap", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Slack.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Slack.app/Contents/MacOS/Slack", "Aero.App.pid" : 80239, "Aero.App.version" : "445000069", "Aero.App.versionShort" : "4.45.69", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 5018, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "1" } ================================================ FILE: axDumps/slack_chat_in_a_separate_window.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=80239}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=80239}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=80239}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "redacted (Thread) - redacted - Slack", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=5021, title=\"\", role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=80239}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Slack", "AXWindows" : [ "AXUIElement(AxWindowId=5021, title=\"redacted (Thread) - redacted - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=5018, title=\"REDACTED (DM) - redacted - Slack [Main] 🏠\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.tinyspeck.slackmacgap", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Slack.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Slack.app/Contents/MacOS/Slack", "Aero.App.pid" : 80239, "Aero.App.version" : "445000069", "Aero.App.versionShort" : "4.45.69", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 5021, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "1" } ================================================ FILE: axDumps/slack_huddle_share_screen_draw_on_screen_fake_window.json5 ================================================ // Slack has a feature to draw on screen. It's implemented via fake window { "AXActivationPoint" : " {value = x:-1.000000 y:1297.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:0.000000 w:2304.000000 h:1296.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93565}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93565}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=93565}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:2304.000000 h:1296.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Slack", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=22408, title=\"\", role=\"AXWebArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=93565}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Slack", "AXWindows" : [ "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=22405, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=22410, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXUIElement(AxWindowId=22392, title=\"* bobko-test-2 (Channel) - redacted - Slack [Main] 匠痔\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.tinyspeck.slackmacgap", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Slack.app/Contents/MacOS/Slack", "Aero.App.pid" : 93565, "Aero.App.version" : "445000069", "Aero.App.versionShort" : "4.45.69", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized", "Aero.axWindowId" : 22405, "Aero.macOS.version" : "Version 15.6 (Build 24G84)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 1000, "Aero.workspace" : "1" } ================================================ FILE: axDumps/slack_huddle_share_screen_floating_popup.json5 ================================================ // Floating popup with buttons like: pen tool, mute, video on/off, end call { "AXActivationPoint" : " {value = x:-1.000000 y:1297.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:1536.000000 y:25.000000 w:620.000000 h:60.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93565}", "AXPosition" : " {value = x:1536.000000 y:25.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93565}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=93565}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:620.000000 h:60.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Slack", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=93565}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Slack", "AXWindows" : [ "AXUIElement(AxWindowId=22408, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=22410, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXUIElement(AxWindowId=22392, title=\"* bobko-test-2 (Channel) - redacted - Slack [Main]\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.tinyspeck.slackmacgap", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Slack.app/Contents/MacOS/Slack", "Aero.App.pid" : 93565, "Aero.App.version" : "445000069", "Aero.App.versionShort" : "4.45.69", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized", "Aero.axWindowId" : 22408, "Aero.macOS.version" : "Version 15.6 (Build 24G84)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 1001, "Aero.workspace" : "1" } ================================================ FILE: axDumps/slack_huddle_share_screen_target_picker.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1297.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:0.000000 w:2304.000000 h:1296.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=82965}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:2304.000000 h:1296.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXSystemDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : null, "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : null, "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=82965}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Slack", "AXWindows" : [ "AXUIElement(AxWindowId=6600, title=\"Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=6155, title=\"\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXUIElement(AxWindowId=6141, title=\"bobko-test-2 (Channel) - JetBrains - Slack\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXMainWindow, get.AXFocusedWindow, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.tinyspeck.slackmacgap", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Slack.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Slack.app/Contents/MacOS/Slack", "Aero.App.pid" : 82965, "Aero.App.version" : "446000096", "Aero.App.versionShort" : "4.46.96", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 6155, "Aero.macOS.version" : "Version 15.7 (Build 24G222)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : 1000, "Aero.workspace" : null } ================================================ FILE: axDumps/spotify.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "https://xpui.app.spotify.com/index.html", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=37876}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=37876}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=37876}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "YONAKA - PREDATOR", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=37876}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Spotify", "AXWindows" : [ "AXUIElement(AxWindowId=14228, title=\"YONAKA - PREDATOR\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.spotify.client", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Spotify.app/Contents/MacOS/Spotify", "Aero.App.version" : "1.2.61.443", "Aero.App.versionShort" : "1.2.61.443", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14228, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "B" } ================================================ FILE: axDumps/spotify_miniplayer.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "about:blank", "AXFocused" : 0, "AXFrame" : " {value = x:1477.000000 y:846.000000 w:300.000000 h:300.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=89246}", "AXPosition" : " {value = x:1477.000000 y:846.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=89246}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=89246}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:300.000000 h:300.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "On Repeat | Spotify Playlist", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=89246}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Spotify", "AXWindows" : [ "AXUIElement(AxWindowId=36854, title=\"On Repeat | Spotify Playlist\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=34090, title=\"Spotify Premium\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.spotify.client", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Spotify.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Spotify.app/Contents/MacOS/Spotify", "Aero.App.pid" : 89246, "Aero.App.version" : "1.2.77.358", "Aero.App.versionShort" : "1.2.77.358", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXSize", "Aero.axWindowId" : 36854, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : "P" } ================================================ FILE: axDumps/steam_1.json5 ================================================ // Steam is weird. It shows one visible window, but creates 2 AX objects. // AeroSpace has to deliberatery ignore the redundant AX object to avoid weird focus issues // ./steam_1.json5 -- valid AX object // ./steam_2.json5 -- weird AX object { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:386.000000 y:131.000000 w:1028.000000 h:864.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=10146}", "AXPosition" : " {value = x:386.000000 y:131.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:1028.000000 h:864.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Steam", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=2170, title=\"Steam\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2170, title=\"Steam\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : null, "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Steam Helper", "AXWindows" : [ "AXUIElement(AxWindowId=2170, title=\"Steam\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXFocusedUIElement, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.valvesoftware.steam.helper", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Library/Application%20Support/Steam/Steam.AppBundle/Steam/Contents/Frameworks/Steam%20Helper.app/Contents/MacOS/Steam%20Helper", "Aero.App.pid" : 10146, "Aero.App.version" : "1650.57", "Aero.App.versionShort" : "31.0.1650.57", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2170, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/steam_2.json5 ================================================ // Weird AX popup { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:-15000.000000 y:16168.000000 w:1.000000 h:1.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=10129}", "AXPosition" : " {value = x:-15000.000000 y:16168.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:1.000000 h:1.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2169, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=2169, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2169, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXMenuBar" : " {pid=10129}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Steam", "AXWindows" : [ "AXUIElement(AxWindowId=2169, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.valvesoftware.steam", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Users/bobko/Library/Application%20Support/Steam/Steam.AppBundle/Steam/Contents/MacOS/steam_osx", "Aero.App.pid" : 10129, "Aero.App.version" : "4.0", "Aero.App.versionShort" : null, "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 2169, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.workspace" : null } ================================================ FILE: axDumps/sublime_text_4.json5 ================================================ { "AXActivationPoint" : " {value = x:70.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=97586}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=97586}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "untitled", "AXTitleUIElement" : "AXUIElement(AxWindowId=11259, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=97586}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Sublime Text", "AXWindows" : [ "AXUIElement(AxWindowId=11259, title=\"untitled\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.sublimetext.4", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Sublime%20Text.app/Contents/MacOS/sublime_text", "Aero.App.version" : "4192", "Aero.App.versionShort" : "Build 4192", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 11259, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "E" } ================================================ FILE: axDumps/system_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:816.000000 y:212.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:806.000000 y:196.000000 w:723.000000 h:973.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "main", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=94208}", "AXPosition" : " {value = x:806.000000 y:196.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=94208}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=94208}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionObject = \" {pid=94208}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=94208}\";\n SectionUniqueID = AXSearch;\n}", "{\n SectionDescription = \"Content Navigator\";\n SectionObject = \" {pid=94208}\";\n SectionUniqueID = AXContentNavigator;\n}" ], "AXSize" : " {value = w:723.000000 h:973.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "", "AXTitleUIElement" : "AXUIElement(AxWindowId=4774, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4774, title=nil, role=\"AXOutline\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=94208}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "System Settings", "AXWindows" : [ "AXUIElement(AxWindowId=4774, title=\"\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.systempreferences", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///System/Applications/System%20Settings.app/", "Aero.App.nsApp.execPath" : "file:///System/Applications/System%20Settings.app/Contents/MacOS/System%20Settings", "Aero.App.pid" : 94208, "Aero.App.version" : "15.0", "Aero.App.versionShort" : "15.0", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4774, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/telegram.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=5573}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=5573}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "‎⁨Saved Messages⁩ – (47104)", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=13215, title=\"\", role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=5573}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Telegram", "AXWindows" : [ "AXUIElement(AxWindowId=13215, title=\"‎⁨Saved Messages⁩ – (47104)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.tdesktop.Telegram", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Telegram.app/Contents/MacOS/Telegram", "Aero.App.version" : "5.14.0", "Aero.App.versionShort" : "5.14.0", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 13215, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "T" } ================================================ FILE: axDumps/telegram_image_viewer.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1297.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:0.000000 w:2304.000000 h:1296.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=75417}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:2304.000000 h:1296.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Media viewer", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4259, title=\"Media viewer\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4259, title=\"Media viewer\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4232, title=\"‎⁨redacted⁩ – (57469)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=75417}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Telegram", "AXWindows" : [ "AXUIElement(AxWindowId=4259, title=\"Media viewer\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=4232, title=\"‎⁨redacted⁩ – (57469)\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.tdesktop.Telegram", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Telegram.app/Contents/MacOS/Telegram", "Aero.App.version" : "5.15.4", "Aero.App.versionShort" : "5.15.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 4259, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : 101, "Aero.workspace" : "T" } ================================================ FILE: axDumps/terminal_app.json5 ================================================ { "AXActivationPoint" : " {value = x:100.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/", "AXFocused" : 0, "AXFrame" : " {value = x:30.000000 y:44.000000 w:1767.000000 h:1113.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "_NS:136", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=43740}", "AXPosition" : " {value = x:30.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : "AXUIElement(AxWindowId=14623, title=\"bobko\", role=\"AXImage\", subrole=nil)", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=43740}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1767.000000 h:1113.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "bobko — -zsh — 251×77", "AXTitleUIElement" : "AXUIElement(AxWindowId=14623, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14623, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=43740}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Terminal", "AXWindows" : [ "AXUIElement(AxWindowId=14623, title=\"bobko — -zsh — 251×77\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.Terminal", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", "Aero.App.version" : "455.1", "Aero.App.versionShort" : "2.14", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14623, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "M" } ================================================ FILE: axDumps/transmission.json5 ================================================ { "AXActivationPoint" : " {value = x:1869.000000 y:1155.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1799.000000 y:1141.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "_NS:14", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=80282}", "AXPosition" : " {value = x:1799.000000 y:1141.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=80282}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=80282}\";\n SectionUniqueID = AXToolbar;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Transmission", "AXTitleUIElement" : "AXUIElement(AxWindowId=11545, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=11545, title=nil, role=\"AXOutline\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=80282}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Transmission", "AXWindows" : [ "AXUIElement(AxWindowId=11545, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=11557, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.m0k.transmission", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Transmission.app/Contents/MacOS/Transmission", "Aero.App.version" : "14714.3.0", "Aero.App.versionShort" : "3.00", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.axWindowId" : 11545, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "B" } ================================================ FILE: axDumps/transmission_torrent_inspector.json5 ================================================ // Double click any torrent { "AXActivationPoint" : " {value = x:546.000000 y:393.500000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:496.000000 y:384.000000 w:921.000000 h:659.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:135", "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=87714}", "AXPosition" : " {value = x:496.000000 y:384.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=87714}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=87714}\";\n SectionUniqueID = AXSearch;\n}" ], "AXSize" : " {value = w:921.000000 h:659.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXFloatingWindow", "AXTitle" : "Files - Torrent Inspector", "AXTitleUIElement" : "AXUIElement(AxWindowId=3008, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXWindow" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=3007, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=87714}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Transmission", "AXWindows" : [ "AXUIElement(AxWindowId=3008, title=\"Files - Torrent Inspector\", role=\"AXWindow\", subrole=\"AXFloatingWindow\")", "AXUIElement(AxWindowId=3007, title=\"Transmission\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.m0k.transmission", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Transmission.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Transmission.app/Contents/MacOS/Transmission", "Aero.App.pid" : 87714, "Aero.App.version" : "14714.3.0", "Aero.App.versionShort" : "3.00", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXSize", "Aero.axWindowId" : 3008, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "B" } ================================================ FILE: axDumps/vlc_empty.json5 ================================================ { "AXActivationPoint" : " {value = x:703.000000 y:795.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:633.000000 y:781.000000 w:725.000000 h:64.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:122", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=38681}", "AXPosition" : " {value = x:633.000000 y:781.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=38681}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:725.000000 h:64.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "VLC media player", "AXTitleUIElement" : "AXUIElement(AxWindowId=14277, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=38681}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=14277, title=nil, role=\"AXTextField\", subrole=\"AXSearchField\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=38681}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "VLC", "AXWindows" : [ "AXUIElement(AxWindowId=14277, title=\"VLC media player\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "org.videolan.vlc", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/VLC.app/Contents/MacOS/VLC", "Aero.App.version" : "3.0.21", "Aero.App.versionShort" : "3.0.21", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14277, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "dialog", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.Workspace", "Aero.workspace" : "F" } ================================================ FILE: axDumps/vlc_fullscreen.json5 ================================================ // non-native fullscreen (default) { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:0.000000 w:1800.000000 h:1169.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=86771}", "AXPosition" : " {value = x:0.000000 y:0.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ ], "AXSize" : " {value = w:1800.000000 h:1169.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=86771}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=4856, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=4856, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4856, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXMenuBar" : " {pid=86771}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "VLC", "AXWindows" : [ "AXUIElement(AxWindowId=2941, title=\"Fullscreen Controls\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=4856, title=\"\", role=\"AXWindow\", subrole=\"AXUnknown\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.videolan.vlc", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/VLC.app/", "Aero.App.nsApp.execPath" : "file:///Applications/VLC.app/Contents/MacOS/VLC", "Aero.App.pid" : 86771, "Aero.App.version" : "3.0.21", "Aero.App.versionShort" : "3.0.21", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", // todo fix "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain", "Aero.axWindowId" : 4856, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/vlc_video_playing.json5 ================================================ { "AXActivationPoint" : " {value = x:70.000000 y:159.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/REDACTED/REDACTED.mkv", "AXFocused" : 1, "AXFrame" : " {value = x:0.000000 y:145.000000 w:1800.000000 h:1024.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "_NS:122", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=86771}", "AXPosition" : " {value = x:0.000000 y:145.000000 type = kAXValueCGPointType}", "AXProxy" : "AXUIElement(AxWindowId=2942, title=\"REDACTED.mkv\", role=\"AXImage\", subrole=nil)", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=86771}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1024.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "REDACTED", "AXTitleUIElement" : "AXUIElement(AxWindowId=2942, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=86771}", "AXFocusedUIElement" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=86771}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "VLC", "AXWindows" : [ "AXUIElement(AxWindowId=2942, title=\"REDACTED\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "org.videolan.vlc", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/VLC.app/", "Aero.App.nsApp.execPath" : "file:///Applications/VLC.app/Contents/MacOS/VLC", "Aero.App.pid" : 86771, "Aero.App.version" : "3.0.21", "Aero.App.versionShort" : "3.0.21", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2942, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "F" } ================================================ FILE: axDumps/vs_code.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:44.000000 w:1800.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=38488}", "AXPosition" : " {value = x:0.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=38488}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=38488}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1800.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Untitled-1", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : null, "AXFocusedWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=38488}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Code", "AXWindows" : [ "AXUIElement(AxWindowId=14272, title=\"Untitled-1\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.microsoft.VSCode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Visual%20Studio%20Code.app/Contents/MacOS/Electron", "Aero.App.version" : "1.99.3", "Aero.App.versionShort" : "1.99.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14272, "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxUiElementWindowType" : "window", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.TilingContainer", "Aero.workspace" : "V" } ================================================ FILE: axDumps/vs_code_nativeFullScreen_false.json5 ================================================ // "window.nativeFullScreen": false { "AXActivationPoint" : " {value = x:349.000000 y:193.000000 type = kAXValueCGPointType}", "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:279.000000 y:179.000000 w:1431.000000 h:926.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=91750}", "AXPosition" : " {value = x:279.000000 y:179.000000 type = kAXValueCGPointType}", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=91750}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=91750}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1431.000000 h:926.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Settings", "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXFocusedWindow" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=91750}", "AXRole" : "AXApplication", "AXTitle" : "Code", "AXWindows" : [ "AXUIElement(AxWindowId=1736, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "AXFunctionRowTopLevelElements, AXFrame, AXFocusedUIElement, AXExtrasMenuBar, AXSize, AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.microsoft.VSCode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Visual%20Studio%20Code.app/Contents/MacOS/Electron", "Aero.App.pid" : 91750, "Aero.App.version" : "1.103.2", "Aero.App.versionShort" : "1.103.2", "Aero.AxFailed" : "AXFrame, AXGrowArea, AXActivationPoint, AXFullScreenButton, AXProxy, AXDefaultButton, AXTitleUIElement, AXCancelButton, AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 1736, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/vs_codium.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=93641}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=93641}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=93641}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Settings", "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXFocusedWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=93641}", "AXRole" : "AXApplication", "AXTitle" : "VSCodium", "AXWindows" : [ "AXUIElement(AxWindowId=1754, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "AXFunctionRowTopLevelElements, AXFrame, AXFocusedUIElement, AXExtrasMenuBar, AXSize, AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.vscodium", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/VSCodium.app/Contents/MacOS/Electron", "Aero.App.pid" : 93641, "Aero.App.version" : "1.103.25610", "Aero.App.versionShort" : "1.103.25610", "Aero.AxFailed" : "AXFrame, AXGrowArea, AXActivationPoint, AXProxy, AXDefaultButton, AXTitleUIElement, AXCancelButton, AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 1754, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/vs_codium_nativeFullScreen_false.json5 ================================================ // "window.nativeFullScreen": false { "AXActivationPoint" : " {value = x:970.000000 y:58.000000 type = kAXValueCGPointType}", "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDocument" : "", "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:900.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=94105}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=94105}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=94105}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:900.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Settings", "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTopLevelUIElement" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXFocusedWindow" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXManualAccessibility" : 0, "AXMenuBar" : " {pid=94105}", "AXRole" : "AXApplication", "AXTitle" : "VSCodium", "AXWindows" : [ "AXUIElement(AxWindowId=1763, title=\"Settings\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "AXFunctionRowTopLevelElements, AXFrame, AXFocusedUIElement, AXExtrasMenuBar, AXSize, AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXManualAccessibility, AXFrontmost" }, "Aero.App.appBundleId" : "com.vscodium", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/VSCodium.app/Contents/MacOS/Electron", "Aero.App.pid" : 94105, "Aero.App.version" : "1.103.25610", "Aero.App.versionShort" : "1.103.25610", "Aero.AxFailed" : "AXFrame, AXGrowArea, AXActivationPoint, AXFullScreenButton, AXProxy, AXDefaultButton, AXTitleUIElement, AXCancelButton, AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 1763, "Aero.macOS.version" : "Version 15.6.1 (Build 24G90)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "1" } ================================================ FILE: axDumps/xcode.json5 ================================================ { "AXActivationPoint" : " {value = x:10.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : "file:///Users/bobko/a/AeroSpace/Sources/AppBundle/tree/MacWindow.swift", "AXFocused" : 0, "AXFrame" : " {value = x:0.000000 y:40.000000 w:1800.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXIdentifier" : "Xcode.WorkspaceWindow", "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=94775}", "AXPosition" : " {value = x:0.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=94775}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionObject = \" {pid=94775}\";\n}", "{\n SectionObject = \" {pid=94775}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=94775}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1800.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "AeroSpace — MacWindow.swift", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=4784, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=94775}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=4784, title=\"AeroSpace — MacWindow.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Xcode.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.pid" : 94775, "Aero.App.version" : "24454", "Aero.App.versionShort" : "26.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 4784, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "X" } ================================================ FILE: axDumps/xcode_build_succeeded_popup.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1170.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:800.000000 y:829.000000 w:200.000000 h:200.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=27529}", "AXPosition" : " {value = x:800.000000 y:829.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=27529}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:200.000000 h:200.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXSystemDialog", "AXTitle" : "Build Succeeded", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=14010, title=nil, role=\"AXTextArea\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=14010, title=\"AeroSpace — AxWindowKindTest.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=14010, title=\"AeroSpace — AxWindowKindTest.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=27529}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=14483, title=\"Build Succeeded\", role=\"AXWindow\", subrole=\"AXSystemDialog\")", "AXUIElement(AxWindowId=14010, title=\"AeroSpace — AxWindowKindTest.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.version" : "23785", "Aero.App.versionShort" : "16.3", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 14483, "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxUiElementWindowType" : "popup", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "AppBundle.MacosPopupWindowsContainer", "Aero.workspace" : null } ================================================ FILE: axDumps/xcode_installing_system_components.json5 ================================================ { "AXActivationPoint" : " {value = x:-1141.000000 y:781.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:-1151.000000 y:767.000000 w:280.000000 h:171.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 1, "AXParent" : " {pid=34828}", "AXPosition" : " {value = x:-1151.000000 y:767.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=34828}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:280.000000 h:171.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "Xcode", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=372, title=\"Xcode\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=372, title=\"Xcode\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=372, title=\"Xcode\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXMenuBar" : " {pid=34828}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=372, title=\"Xcode\", role=\"AXWindow\", subrole=\"AXDialog\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.version" : "23792", "Aero.App.versionShort" : "16.4", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.axWindowId" : 372, "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.workspace" : "D" } ================================================ FILE: axDumps/xcode_open_quickly.json5 ================================================ // cmd-shift-o { "AXActivationPoint" : " {value = x:910.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:900.000000 y:44.000000 w:454.000000 h:47.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "open_quickly", "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=76223}", "AXPosition" : " {value = x:900.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXSearch;\n}" ], "AXSize" : " {value = w:454.000000 h:47.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=28188, title=nil, role=\"AXTextField\", subrole=\"AXSearchField\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=76223}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=28188, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Xcode.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.pid" : 76223, "Aero.App.version" : "24455", "Aero.App.versionShort" : "26.1.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXSize", "Aero.axWindowId" : 28188, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "X" } ================================================ FILE: axDumps/xcode_quick_actions.json5 ================================================ // cmd-shift-a { "AXActivationPoint" : " {value = x:630.000000 y:328.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:620.000000 y:314.000000 w:560.000000 h:47.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "open_quickly", "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=76223}", "AXPosition" : " {value = x:620.000000 y:314.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Search;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXSearch;\n}" ], "AXSize" : " {value = w:560.000000 h:47.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=28220, title=nil, role=\"AXTextField\", subrole=\"AXSearchField\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=76223}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=28220, title=\"\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Xcode.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.pid" : 76223, "Aero.App.version" : "24455", "Aero.App.versionShort" : "26.1.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXSize", "Aero.axWindowId" : 28220, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "X" } ================================================ FILE: axDumps/xcode_settings.json5 ================================================ { "AXActivationPoint" : " {value = x:348.000000 y:213.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:338.000000 y:199.000000 w:959.000000 h:516.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 0, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=76223}", "AXPosition" : " {value = x:338.000000 y:199.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionDescription = Toolbar;\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXToolbar;\n}", "{\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContainer;\n}", "{\n SectionDescription = \"Content Navigator\";\n SectionObject = \" {pid=76223}\";\n SectionUniqueID = AXContentNavigator;\n}" ], "AXSize" : " {value = w:959.000000 h:516.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXDialog", "AXTitle" : "General", "AXTitleUIElement" : "AXUIElement(AxWindowId=28237, title=nil, role=\"AXStaticText\", subrole=nil)", "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXRole" : "AXButton", "AXSubrole" : "AXZoomButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXWindow" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=28237, title=nil, role=\"AXOutline\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=76223}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=28237, title=\"General\", role=\"AXWindow\", subrole=\"AXDialog\")", "AXUIElement(AxWindowId=28187, title=\"AeroSpace — AeroSpace.xcodeproj\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Xcode.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.pid" : 76223, "Aero.App.version" : "24455", "Aero.App.versionShort" : "26.1.1", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "dialog", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections, AXMinimized, AXSize", "Aero.axWindowId" : 28237, "Aero.macOS.version" : "Version 15.7.1 (Build 24G231)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.Workspace)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "X" } ================================================ FILE: axDumps/xcode_welcome_window.json5 ================================================ { "AXActivationPoint" : " {value = x:541.000000 y:657.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:530.000000 y:208.000000 w:740.000000 h:460.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXIdentifier" : "Welcome", "AXMain" : 0, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=7108}", "AXPosition" : " {value = x:530.000000 y:208.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=7108}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:740.000000 h:460.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXUnknown", "AXTitle" : "Welcome to Xcode", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=794, title=nil, role=\"AXOutline\", subrole=nil)", "AXFocusedWindow" : "AXUIElement(AxWindowId=794, title=\"Welcome to Xcode\", role=\"AXWindow\", subrole=\"AXUnknown\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : null, "AXMenuBar" : " {pid=7108}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Xcode", "AXWindows" : [ "AXUIElement(AxWindowId=794, title=\"Welcome to Xcode\", role=\"AXWindow\", subrole=\"AXUnknown\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXMainWindow, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "com.apple.dt.Xcode", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Xcode.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Xcode.app/Contents/MacOS/Xcode", "Aero.App.pid" : 7108, "Aero.App.version" : "24553", "Aero.App.versionShort" : "26.2", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXMinimizeButton, get.AXDocument, get.AXCloseButton, isWritable.AXActivationPoint, get.AXFullScreenButton, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXZoomButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", // todo fix "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXPosition, AXSections", "Aero.axWindowId" : 794, "Aero.macOS.version" : "Version 26.2 (Build 25C56)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : null } ================================================ FILE: axDumps/zebar.json5 ================================================ { "AXActivationPoint" : " {value = x:-1.000000 y:1441.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : null, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:1151.000000 y:40.000000 w:3600.000000 h:80.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : null, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : null, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=5114}", "AXPosition" : " {value = x:1151.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=5114}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:3600.000000 h:80.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Zebar - glzr-io.starter / vanilla", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : null, "Aero.AXApp" : { "AXExtrasMenuBar" : " {pid=5114}", "AXFocusedUIElement" : " {pid=5122}", "AXFocusedWindow" : "AXUIElement(AxWindowId=10742, title=\"Zebar - glzr-io.starter / vanilla\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=10742, title=\"Zebar - glzr-io.starter / vanilla\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=5114}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Zebar", "AXWindows" : [ "AXUIElement(AxWindowId=10742, title=\"Zebar - glzr-io.starter / vanilla\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=10743, title=\"Zebar - glzr-io.starter / vanilla\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "com.glzr.zebar", "Aero.App.nsApp.activationPolicy" : "accessory", "Aero.App.nsApp.execPath" : "file:///Applications/Zebar.app/Contents/MacOS/zebar", "Aero.App.version" : "20250618.170017", "Aero.App.versionShort" : "3.1.1", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.axWindowId" : 10742, "Aero.macOS.version" : "Version 26.0 (Build 25A354)", "Aero.on-window-detected" : [ { "commands" : "layout tiling", "matcher" : "" } ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "1" } ================================================ FILE: axDumps/zed.json5 ================================================ { "AXActivationPoint" : " {value = x:100.000000 y:58.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:30.000000 y:44.000000 w:1770.000000 h:1124.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=87284}", "AXPosition" : " {value = x:30.000000 y:44.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=87284}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1770.000000 h:1124.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "AeroSpace — accessibility.swift", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=87284}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Zed", "AXWindows" : [ "AXUIElement(AxWindowId=11823, title=\"AeroSpace — accessibility.swift\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden" }, "Aero.App.appBundleId" : "dev.zed.Zed", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.execPath" : "file:///Applications/Zed.app/Contents/MacOS/zed", "Aero.App.version" : "20250718.173548", "Aero.App.versionShort" : "0.195.5", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.axWindowId" : 11823, "Aero.macOS.version" : "Version 15.5 (Build 24F74)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "E" } ================================================ FILE: axDumps/zen_browser.json5 ================================================ { "AXActivationPoint" : " {value = x:40.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 1, "AXFrame" : " {value = x:30.000000 y:40.000000 w:1770.000000 h:1128.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23963}", "AXPosition" : " {value = x:30.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23963}\";\n SectionUniqueID = AXContent;\n}" ], "AXSize" : " {value = w:1770.000000 h:1128.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Zen Browser", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2621, title=\"\", role=\"AXGroup\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23963}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Zen", "AXWindows" : [ "AXUIElement(AxWindowId=2621, title=\"Zen Browser\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "app.zen-browser.zen", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Zen.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Zen.app/Contents/MacOS/zen", "Aero.App.pid" : 23963, "Aero.App.version" : "125.11.9", "Aero.App.versionShort" : "1.17.6b", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "window", "Aero.AxUiElementWindowType_isDialogHeuristic" : false, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXMinimized, AXSize", "Aero.axWindowId" : 2621, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.TilingContainer)", "Aero.windowLevel" : "normalWindow", "Aero.workspace" : "1" } ================================================ FILE: axDumps/zen_browser_pip.json5 ================================================ { "AXActivationPoint" : " {value = x:40.000000 y:56.000000 type = kAXValueCGPointType}", "AXCancelButton" : null, "AXCloseButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXCloseButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription, AXEdited" }, "AXDefaultButton" : null, "AXDocument" : null, "AXFocused" : 0, "AXFrame" : " {value = x:30.000000 y:40.000000 w:1769.000000 h:995.000000 type = kAXValueCGRectType}", "AXFullScreen" : 0, "AXFullScreenButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "AXGrowArea" : null, "AXMain" : 1, "AXMinimizeButton" : { "AXEnabled" : 0, "AXParent" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXMinimizeButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXHelp, AXPosition, AXRoleDescription" }, "AXMinimized" : 0, "AXModal" : 0, "AXParent" : " {pid=23963}", "AXPosition" : " {value = x:30.000000 y:40.000000 type = kAXValueCGPointType}", "AXProxy" : null, "AXRole" : "AXWindow", "AXSections" : [ "{\n SectionDescription = Content;\n SectionObject = \" {pid=23963}\";\n SectionUniqueID = AXContent;\n}", "{\n SectionObject = \" {pid=23963}\";\n SectionUniqueID = AXContainer;\n}" ], "AXSize" : " {value = w:1769.000000 h:995.000000 type = kAXValueCGSizeType}", "AXSubrole" : "AXStandardWindow", "AXTitle" : "Picture-in-Picture", "AXTitleUIElement" : null, "AXToolbarButton" : null, "AXZoomButton" : { "AXEnabled" : 1, "AXParent" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXRole" : "AXButton", "AXSubrole" : "AXFullScreenButton", "AXTitle" : null, "AXTopLevelUIElement" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "Aero.AxFailed" : "get.AXTitle", "Aero.AxIgnored" : "AXFrame, AXSize, AXFocused, AXChildren, AXHelp, AXPosition, AXRoleDescription" }, "Aero.AXApp" : { "AXExtrasMenuBar" : null, "AXFocusedUIElement" : "AXUIElement(AxWindowId=2651, title=\"\", role=\"AXGroup\", subrole=\"AXUnknown\")", "AXFocusedWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXFrame" : null, "AXFrontmost" : 1, "AXFunctionRowTopLevelElements" : [ ], "AXMainWindow" : "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXMenuBar" : " {pid=23963}", "AXPosition" : null, "AXRole" : "AXApplication", "AXSize" : null, "AXTitle" : "Zen", "AXWindows" : [ "AXUIElement(AxWindowId=2651, title=\"Picture-in-Picture\", role=\"AXWindow\", subrole=\"AXStandardWindow\")", "AXUIElement(AxWindowId=2621, title=\"Danila Poperechny: \"AGENT 813\" | stand-up, 2025 - YouTube\", role=\"AXWindow\", subrole=\"AXStandardWindow\")" ], "Aero.AxFailed" : "isWritable.AXFunctionRowTopLevelElements, get.AXFrame, isWritable.AXFrame, get.AXExtrasMenuBar, isWritable.AXExtrasMenuBar, get.AXSize, isWritable.AXSize, get.AXPosition, isWritable.AXPosition", "Aero.AxIgnored" : "AXChildren, AXChildrenInNavigationOrder, AXEnhancedUserInterface, AXPreferredLanguage, AXRoleDescription, AXHidden", "Aero.AxWritable" : "AXFrontmost" }, "Aero.App.appBundleId" : "app.zen-browser.zen", "Aero.App.nsApp.activationPolicy" : "regular", "Aero.App.nsApp.appBundlePath" : "file:///Applications/Zen.app/", "Aero.App.nsApp.execPath" : "file:///Applications/Zen.app/Contents/MacOS/zen", "Aero.App.pid" : 23963, "Aero.App.version" : "125.11.9", "Aero.App.versionShort" : "1.17.6b", "Aero.AxFailed" : "isWritable.AXFrame, get.AXGrowArea, get.AXDocument, isWritable.AXActivationPoint, get.AXProxy, get.AXDefaultButton, get.AXTitleUIElement, get.AXCancelButton, get.AXToolbarButton", "Aero.AxIgnored" : "AXChildrenInNavigationOrder, AXChildren, AXRoleDescription", "Aero.AxUiElementWindowType" : "popup", "Aero.AxUiElementWindowType_isDialogHeuristic" : true, "Aero.AxWritable" : "AXFullScreen, AXPosition, AXSections, AXMain, AXSize", "Aero.axWindowId" : 2651, "Aero.macOS.version" : "Version 26.1 (Build 25B78)", "Aero.on-window-detected" : [ ], "Aero.treeNodeParent" : "Optional(AppBundle.MacosPopupWindowsContainer)", "Aero.windowLevel" : "alwaysOnTopWindow", "Aero.workspace" : null } ================================================ FILE: build-debug.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh # 1. It takes 300ms for cmd-help to generate. It's too long to run in build-debug.sh, that's why we ignore it # 2. Grammar is not expected to change very often, that's why it's not regenerated by default ./generate.sh --ignore-xcodeproj --ignore-cmd-help --ignore-shell-parser swift build "$@" swift build --target AppBundleTests "$@" # swift build doesn't build test targets by default :( rm -rf .debug && mkdir .debug cp -r .build/debug/aerospace .debug cp -r .build/debug/AeroSpaceApp .debug ================================================ FILE: build-docs.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./script/install-dep.sh --bundler rm -rf .site && mkdir .site rm -rf .man && mkdir .man cp-docs() { cp -r ./docs/*.adoc "$1" cp -r ./docs/assets "$1" cp -r ./docs/util "$1" cp -r ./docs/config-examples "$1" } build-site() { cp-docs ./.site cp ./docs/index.html ./.site cd .site # Delete "aerospace " prefifx in synopsis sed -E -i '' '/tag::synopsis/, /end::synopsis/ s/^(aerospace | {10})//' aerospace* bundler exec asciidoctor ./guide.adoc ./commands.adoc ./goodies.adoc cp goodies.html goodness.html # backwards compatibility rm -rf ./*.adoc cd - > /dev/null git rev-parse HEAD > .site/version.html if ! test -z "$(git status --porcelain)"; then echo "git working directory is dirty" >> .site/version.html fi } build-man() { cp-docs .man cd .man bundler exec asciidoctor -b manpage aerospace*.adoc rm -rf -- *.adoc cd - > /dev/null } build-site build-man ================================================ FILE: build-release.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh build_version="0.0.0-SNAPSHOT" codesign_identity="aerospace-codesign-certificate" while test $# -gt 0; do case $1 in --build-version) build_version="$2"; shift 2;; --codesign-identity) codesign_identity="$2"; shift 2;; *) echo "Unknown option $1" > /dev/stderr; exit 1 ;; esac done ############# ### BUILD ### ############# ./build-docs.sh ./build-shell-completion.sh ./generate.sh ./script/check-uncommitted-files.sh ./generate.sh --build-version "$build_version" --codesign-identity "$codesign_identity" --generate-git-hash swift build -c release --arch arm64 --arch x86_64 --product aerospace -Xswiftc -warnings-as-errors # CLI # todo: make xcodebuild use the same toolchain as swift # toolchain="$(plutil -extract CFBundleIdentifier raw ~/Library/Developer/Toolchains/swift-6.1-RELEASE.xctoolchain/Info.plist)" # xcodebuild -toolchain "$toolchain" \ # Unfortunately, Xcode 16 fails with: # 2025-05-05 15:51:15.618 xcodebuild[4633:13690815] Writing error result bundle to /var/folders/s1/17k6s3xd7nb5mv42nx0sd0800000gn/T/ResultBundle_2025-05-05_15-51-0015.xcresult # xcodebuild: error: Could not resolve package dependencies: # :0: warning: legacy driver is now deprecated; consider avoiding specifying '-disallow-use-new-driver' # :0: error: unable to execute command: rm -rf .release && mkdir .release xcode_configuration="Release" xcodebuild -version xcodebuild-pretty .release/xcodebuild.log clean build \ -scheme AeroSpace \ -destination "generic/platform=macOS" \ -configuration "$xcode_configuration" \ -derivedDataPath .xcode-build git checkout . cp -r ".xcode-build/Build/Products/$xcode_configuration/AeroSpace.app" .release cp -r .build/apple/Products/Release/aerospace .release ################ ### SIGN CLI ### ################ codesign -s "$codesign_identity" .release/aerospace ################ ### VALIDATE ### ################ expected_layout=$(cat < /dev/null; then echo "$1 doesn't contain $hash" exit 1 fi } check-universal-binary .release/AeroSpace.app/Contents/MacOS/AeroSpace check-universal-binary .release/aerospace check-contains-hash .release/AeroSpace.app/Contents/MacOS/AeroSpace check-contains-hash .release/aerospace codesign -v .release/AeroSpace.app codesign -v .release/aerospace ############ ### PACK ### ############ mkdir -p ".release/AeroSpace-v$build_version/manpage" && cp .man/*.1 ".release/AeroSpace-v$build_version/manpage" cp -r ./legal ".release/AeroSpace-v$build_version/legal" cp -r .shell-completion ".release/AeroSpace-v$build_version/shell-completion" cd .release mkdir -p "AeroSpace-v$build_version/bin" && cp -r aerospace "AeroSpace-v$build_version/bin" cp -r AeroSpace.app "AeroSpace-v$build_version" zip -r "AeroSpace-v$build_version.zip" "AeroSpace-v$build_version" cd - ################# ### Brew Cask ### ################# for cask_name in aerospace aerospace-dev; do ./script/build-brew-cask.sh \ --cask-name "$cask_name" \ --zip-uri ".release/AeroSpace-v$build_version.zip" \ --build-version "$build_version" done ================================================ FILE: build-shell-completion.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./script/install-dep.sh --complgen rm -rf .shell-completion && mkdir -p \ .shell-completion/zsh \ .shell-completion/fish \ .shell-completion/bash ./.deps/cargo-root/bin/complgen aot ./grammar/commands-bnf-grammar.txt \ --zsh-script .shell-completion/zsh/_aerospace \ --fish-script .shell-completion/fish/aerospace.fish \ --bash-script .shell-completion/bash/aerospace if ! (not-outdated-bash --version | grep -q 'version 5'); then echo "bash version is too old. At least version 5 is required" > /dev/stderr exit 1 fi # Check basic syntax zsh -c 'autoload -Uz compinit; compinit; source ./.shell-completion/zsh/_aerospace' fish -c 'source ./.shell-completion/fish/aerospace.fish' not-outdated-bash -c 'source ./.shell-completion/bash/aerospace' ================================================ FILE: dev-docs/architecture.md ================================================ # Architecture ## Definitions **SPM.** Swift package manager and Swift build tool. In other words, `swift` CLI tool ## High level project infrastructure overview - `../Sources`. The majority of AeroSpace source code. Managed by SPM `../Package.swift` - `../Sources/AppBundle/`. AeroSpace.app server. Technically, it's a SPM library that is exposed to `xcode-app-bundle-launcher` - `../Sources/Cli/`. CLI client. CLI client is built purely using SPM, no Xcode involved (phew!) - `../Sources/Common/`. Shared code between server and client. Mainly command line args parsing and util functions are shared. - `../xcode-app-bundle-launcher/`. Very small technical directory that defines entry point for Xcode project. Xcode projects are ugly and hard to manage without using the Xcode itself. And Swift LSP doesn't support Xcode projects, it only support SPM projects. Unfortunately, SPM doesn't allow building macOS apps (aka "App Bundle"). SPM is only capable of defining libraries and building CLI apps. All code is pushed as much as possible to SPM "library" located in `../Sources/`. The Xcode project model is located in `../AeroSpace.xcodeproj/` and generated by `../project.yml` "skeleton" - `../Sources/AppBundleTests/`. Tests - `../docs/`. Documentation sources for site and man pages in Asciidoc format https://asciidoc.org/ ## client/server interaction `aerospace` CLI binary is client. `AeroSpace.app` is server. Client and server talk to each other via predefined UNIX file. Each time you run a CLI command: 1. Args are parsed by the client, args parsing errors are reported if any. Help is shown if `-h`/`--help` is passed. 1. If args are parsed successfully, the args are sent to the server 1. Server parses the args once again, and runs the command 1. Server returns stdout, stderr, and exit code to the client 1. Client shows stdout, stderr, and ends the process with the requested exit code ## Commands subsystem todo ../Sources/AppBundle/command/ ../Sources/Common/cmdArgs/ Command checklist: - [ ] Documentation in `../docs/aerospace-*` and `../docs/commands.adoc` - [ ] Check that site looks alright `./.site/commands.html` - [ ] Check that man page looks alright `./.man` - [ ] Do `--window-id` and/or `--workspace` flags make sense for the command? - [ ] Shell completion `../grammar/commands-bnf-grammar.txt` ## TOML Config parse subsystem todo ../Sources/AppBundle/config/ ## Tree Model subsystem todo ../Sources/AppBundle/tree/ ## Layout subsystem todo ../Sources/AppBundle/layout/ ================================================ FILE: dev-docs/development.md ================================================ # Development Notes To build/install from sources do the following: 1. Install dependencies 2. Create codesign certificate in `Keychain Access.app` 3. Run one of the entry point scripts to build/install from sources If you struggle to build AeroSpace locally, you can also refer to [builds in GitHub Actions](https://github.com/nikitabobko/AeroSpace/actions?query=branch%3Amain) ## Definitions **SPM.** Swift package manager and Swift build tool. In other words, `swift` CLI tool ## 1. Install dependencies 1. Install Xcode from App Store https://apps.apple.com/us/app/xcode/id497799835 2. Install [swiftly](https://github.com/swiftlang/swiftly). Swiftly is a Swift toolchain manager that will make sure that you use the same swift version as written in `.swift-version` file. `brew install swiftly` 3. If you want to build shell completion, install rust, bash and fish - Install Rust using rustup. https://www.rust-lang.org/tools/install - `brew install bash fish` 4. If you want to build man pages, install Ruby >= 3.0. I recommend using [rbenv](https://github.com/rbenv/rbenv). - `rbenv install 3.3.4` (or whatever 3.x version) - Install asciidoctor using Ruby `bundler`. `cd AeroSpace && bundler install` 5. Install optional `xcbeautify` to make Xcode build logs readable. `brew install xcbeautify` ## 2. Create codesign certificate If you want to run AeroSpace as App Bundle (AeroSpace.app) you need to create self-signed certificate that will be used to codesign AeroSpace. Release artifact is built as App Bundle. If you only plan to build the debug version of AeroSpace, you can run it from the terminal and custom certificate is not required. 1. Open `Keychain Access.app` 2. Menu -> `Keychain Access` -> `Certificate Assistance` -> `Create a Certificate...` - Name: `aerospace-codesign-certificate` - Identity Type: `Self-Signed Root` - Certificate Type: `Code Signing` ## 3. Entry point scripts **Debug build** - `build-debug.sh` - Build debug build to `.debug` dir by using SPM. (Xcode is not involved) - `run-tests.sh` - Run tests. - `swiftformat.sh` - Format the code. - `run-debug.sh` - Run AeroSpace.app debug build. - `run-cli.sh` - Run `aerospace` in CLI. Arguments are forwarded to `aerospace` binary. - `build-docs.sh` - Build the site and man pages to `.site` and `.man` dirs respectively. - `build-shell-completion.sh` - Build shell completion to `.shell-completion`. You can test that the completion works properly by sourcing the file `source ./.shell-completion/zsh/_aerospace` - `generate.sh` - Regenerate generated project files. `AeroSpace.xcodeproj` is generated, and some of the source files (the source files have `Generated` suffix in their names). **Release build** - `build-release.sh` - Build release build to `.release` dir by using Xcode. - `install-from-sources.sh` - Build release build from sources and install it as `aerospace-dev` brew cask. This script is "work in progress". Use it on your own risk. ## IDE - You can obviously [open the project in Xcode](#xcode). - You can use your editor of choice (Neovim, Vim, Emacs, Sublime, VS Code) by using [sourcekit-lsp LSP](https://github.com/apple/sourcekit-lsp). I only tested it in Neovim - AppCode. The initial codebase was written in AppCode and the IDE was pretty solid. But AppCode was unfortunately sunsetted, and it started falling apart. Last time I checked it, it didn't support Swift 5.9 features, and I couldn't make it reliably import the project. RIP ## Xcode Even if you use LSP and another text editor, Xcode is still useful to attach debugger (though you can use `lldb` in CLI). 1. To open the project in Xcode: File -> Open -> Choose `Package.swift` file instead of `AeroSpace.xcodeproj`. It's better to open `Package.swift`, because SPM project is more lightweight. `AeroSpace.xcodeproj` is only used in `*release*.sh` build scripts. 2. After you opened the project in Xcode. Edit Scheme... -> Options -> Console -> Choose `Terminal`. This way Accessibility permission will be requested from Terminal. If you don't change Console to `Terminal`, Accessibility permission will be requested on every rebuild, because the debug binary is unsigned. ## Tips - Use built-in "Accessibility Inspector.app" to inspect accessibility properties of windows - Use [DeskPad](https://github.com/Stengo/DeskPad) or [BetterDisplay 2](https://github.com/waydabber/BetterDisplay) to emulate several monitors - You can use `script/clean-project.sh` to clean the project when something goes wrong. ================================================ FILE: docs/aerospace-balance-sizes.adoc ================================================ = aerospace-balance-sizes(1) include::util/man-attributes.adoc[] :manname: aerospace-balance-sizes // tag::purpose[] :manpurpose: Balance sizes of all windows in the current workspace // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace balance-sizes [-h|--help] [--workspace ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --workspace :: include::./util/workspace-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-close-all-windows-but-current.adoc ================================================ = aerospace-close-all-windows-but-current(1) include::util/man-attributes.adoc[] :manname: aerospace-close-all-windows-but-current // tag::purpose[] :manpurpose: On the focused workspace, close all windows but current // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace close-all-windows-but-current [-h|--help] [--quit-if-last-window] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --quit-if-last-window:: Quit the apps instead of closing them if it's their last window // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-close.adoc ================================================ = aerospace-close(1) include::util/man-attributes.adoc[] :manname: aerospace-close // tag::purpose[] :manpurpose: Close the focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace close [-h|--help] [--quit-if-last-window] [--window-id ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Normally, you don’t need to use this command, because macOS offers its own `cmd+w` binding. You might want to use the command from CLI for scripting purposes // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --quit-if-last-window:: Quit the app instead of closing if it's the last window of the app --window-id :: include::./util/window-id-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-config.adoc ================================================ = aerospace-config(1) include::util/man-attributes.adoc[] :manname: aerospace-config // tag::purpose[] :manpurpose: Query AeroSpace config options // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace config [-h|--help] --get [--json] [--keys] aerospace config [-h|--help] --major-keys aerospace config [-h|--help] --all-keys aerospace config [-h|--help] --config-path // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} For now, only `mode.*` config options are supported Under the hood, the config is represented as recursive data structure of maps, arrays, strings, and integers. Printing without `--json` or `--keys` flag is supported only for scalar types (strings and integers) and array of scalar types. Printing other complicated objects requires `--json` or `--keys` flag. // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --get :: Get the value for a given key. You can inspect available keys with `--major-keys` or `--all-keys` --major-keys:: Print major keys --all-keys:: Print all available keys recursively --json:: Print result in JSON format --keys:: Print keys of the complicated object (map or array) --config-path:: Print absolute path to the loaded config // =========================================================== Examples include::util/conditional-examples-header.adoc[] * List all binding modes: + ---- $ aerospace config --get mode --keys main service ---- * List all key bindings for 'main' binding mode: + ---- $ aerospace config --get mode.main.binding --keys alt-1 alt-2 ... ---- * List all key bindings for 'main' binding mode in JSON format: + ---- $ aerospace config --get mode.main.binding --json { "alt-w" : "workspace W", "alt-y" : "workspace Y", "alt-n" : "workspace N", "alt-shift-e" : "move-node-to-workspace E", "alt-shift-m" : "move-node-to-workspace M", "alt-shift-t" : "move-node-to-workspace T", ... ---- // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-debug-windows.adoc ================================================ = aerospace-debug-windows(1) include::util/man-attributes.adoc[] :manname: aerospace-debug-windows // tag::purpose[] :manpurpose: Interactive command to record Accessibility API debug information to create bug reports // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace debug-windows [-h|--help] [--window-id ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Use this command output to report bug reports about incorrect windows handling (e.g. some windows are floated when they shouldn't). The intended usage is the following: . Run the command to start the debug session recording . Focus problematic window or make the window appear. . Run the command one more time to stop the debug session recording and print the results `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. // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: Print debug information of the specified window right away. Usage of this flag disables interactive mode. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-enable.adoc ================================================ = aerospace-enable(1) include::util/man-attributes.adoc[] :manname: aerospace-enable // tag::purpose[] :manpurpose: Temporarily disable window management // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace enable [-h|--help] toggle aerospace enable [-h|--help] on [--fail-if-noop] aerospace enable [-h|--help] off [--fail-if-noop] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} When you disable AeroSpace, windows from currently invisible workspaces will be placed to the visible area of the screen Key events are not intercepted when AeroSpace is disabled // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --fail-if-noop:: Exit with non-zero exit code if already in the requested mode // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-exec-and-forget.adoc ================================================ = aerospace-exec-and-forget(1) include::util/man-attributes.adoc[] :manname: aerospace-exec-and-forget // tag::purpose[] :manpurpose: Run /bin/bash -c '' // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace exec-and-forget // end::synopsis[] // =========================================================== Description == Description // tag::body[] Run `/bin/bash -c ''`, and don't wait for the command termination. Stdout, stderr and exit code are ignored. For example, you can use this command to launch applications: [source,toml] ---- alt-enter = 'exec-and-forget open -n /System/Applications/Utilities/Terminal.app' ---- `` is passed "as is" to bash without any transformations and escaping. `` is treated as suffix of the TOML string, it's not even an argument in classic CLI sense * The command is available in config * The command is *NOT* available in CLI // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-flatten-workspace-tree.adoc ================================================ = aerospace-flatten-workspace-tree(1) include::util/man-attributes.adoc[] :manname: aerospace-flatten-workspace-tree // tag::purpose[] :manpurpose: Flatten the tree of the focused workspace // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace flatten-workspace-tree [-h|--help] [--workspace ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} The command is useful when you messed up with your layout, and it's easier to "reset" it and start again. // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --workspace :: include::./util/workspace-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-focus-back-and-forth.adoc ================================================ = aerospace-focus-back-and-forth(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Switch between the current and previously focused elements back and forth // end::purpose[] :manname: aerospace-focus-back-and-forth // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace focus-back-and-forth [-h|--help] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose}. The element is either a window or an empty workspace. AeroSpace stores only one previously focused window in history, which means that if you close the previous window, `focus-back-and-forth` has no window to switch focus to. In that case, the command will exit with non-zero exit code. That's why it may be preferred to combine `focus-back-and-forth` with `workspace-back-and-forth`: + ---- aerospace focus-back-and-forth || aerospace workspace-back-and-forth ---- Also see: <> // end::body[] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-focus-monitor.adoc ================================================ = aerospace-focus-monitor(1) include::util/man-attributes.adoc[] :manname: aerospace-focus-monitor // tag::purpose[] :manpurpose: Focus monitor by relative direction, by order, or by pattern // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace focus-monitor [-h|--help] [--wrap-around] (left|down|up|right) aerospace focus-monitor [-h|--help] [--wrap-around] (next|prev) aerospace focus-monitor [-h|--help] ... // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --wrap-around:: Make it possible to wrap around focus // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (left|down|up|right):: Focus monitor in direction relative to the focused monitor (next|prev):: Focus next|prev monitor in order they appear in tray icon ...:: Find the first matching monitor and focus it. Multiple monitor patterns is useful for different monitor configurations. Monitor patterns follow the same format as in `workspace-to-monitor-force-assignment` config option // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-focus.adoc ================================================ = aerospace-focus(1) include::util/man-attributes.adoc[] :manname: aerospace-focus // tag::purpose[] :manpurpose: Set focus to a window. // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace focus [-h|--help] [--ignore-floating] [--wrap-around] [--boundaries ] [--boundaries-action ] (left|down|up|right) aerospace focus [-h|--help] [--ignore-floating] [--wrap-around] [--boundaries ] [--boundaries-action ] (dfs-next|dfs-prev) aerospace focus [-h|--help] --window-id aerospace focus [-h|--help] --dfs-index // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Contrary to i3, `focus` command doesn't have a separate argument to focus floating windows. From `focus` command perspective, floating windows are part of xref:guide.adoc#tree[the tree]. The floating window parent container is determined as the smallest tiling container that contains the center of the floating window. The technique eliminates the need for an additional binding for floating windows. This behavior can be disabled with `--ignore-floating` flag. `focus child|parent` isn't supported because the necessity of this operation is under the question. https://github.com/nikitabobko/AeroSpace/issues/5 // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --boundaries :: Defines focus boundaries. + `` possible values: `(workspace|all-monitors-outer-frame)`. + The default is: `workspace` --boundaries-action :: Defines the behavior when requested to cross the ``. + `` possible values: `(stop|fail|wrap-around-the-workspace|wrap-around-all-monitors)`. + The default is: `stop` --wrap-around:: An alias for `--boundaries-action wrap-around-the-workspace`. The flag is incompatible with either `--boundaries-action` or `--boundaries`. --window-id :: Focus the window with specified `` --dfs-index :: Focus window by its index, based on a depth-first search (DFS) of the window within the workspace tree. Index is 0-based. --ignore-floating:: Don't perceive floating windows as part of the tree. It may be useful for more reliable scripting. // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (left|down|up|right):: Set focus to the nearest window in the given direction. (dfs-next|dfs-prev):: Set focus to the window before or after the current window in the depth-first order (top-to-bottom and left-to-right) of windows in the current workspace tree. In this mode, `--boundaries` must be `workspace` (the default) and `--boundaries-action` can be set to one of `(stop|fail|wrap-around-the-workspace)`. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-fullscreen.adoc ================================================ = aerospace-fullscreen(1) include::util/man-attributes.adoc[] :manname: aerospace-fullscreen // tag::purpose[] :manpurpose: Toggle the fullscreen mode for the focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace fullscreen [-h|--help] [--window-id ] [--no-outer-gaps] aerospace fullscreen [-h|--help] on [--window-id ] [--no-outer-gaps] [--fail-if-noop] aerospace fullscreen [-h|--help] off [--window-id ] [--fail-if-noop] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Switching to a different tiling window within the same workspace while the current focused window is in fullscreen mode results in the fullscreen window exiting fullscreen mode. // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --no-outer-gaps:: Remove the outer gaps when in fullscreen mode --fail-if-noop:: Exit with non-zero exit code if already fullscreen or already not fullscreen --window-id :: include::./util/window-id-flag-desc.adoc[] // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] on, off:: `on` means enter fullscreen mode. `off` means exit fullscreen mode. Toggle between the two if not specified // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-join-with.adoc ================================================ = aerospace-join-with(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Put the focused window and the nearest node in the specified direction under a common parent container // end::purpose[] :manname: aerospace-join-with // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace join-with [-h|--help] [--window-id ] (left|down|up|right) // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Examples include::util/conditional-examples-header.adoc[] Given this layout ---- h_tiles ├── window 1 ├── window 2 (focused) └── window 3 ---- `join-with right` will result in the following layout ---- h_tiles ├── window 1 └── v_tiles ├── window 2 (focused) └── window 3 ---- NOTE: `join-with` is a high-level replacement for i3's https://i3wm.org/docs/userguide.html#_splitting_containers[split command]. There is an observation that the only reason why you might want to split a node is to put several windows under a common "umbrella" parent. Unlike `split`, `join-with` can be used with xref:guide.adoc#normalization[`enable-normalization-flatten-containers`] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-layout.adoc ================================================ = aerospace-layout(1) include::util/man-attributes.adoc[] :manname: aerospace-layout // tag::purpose[] :manpurpose: Change layout of the focused window to the given layout // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace layout [-h|--help] [--window-id ] (h_tiles|v_tiles|h_accordion|v_accordion|tiles|accordion|horizontal|vertical|tiling|floating)... // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} If several arguments are supplied then finds the first argument that doesn't describe the currently active layout, and applies the layout. * Change both tiling layout and orientation in one go: `h_tiles|v_tiles|h_accordion|v_accordion` * Change tiling layout but preserve orientation: `tiles|accordion` * Change orientation but preserve layout: `horizontal|vertical` * Toggle floating/tiling mode: `tiling|floating` // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] // =========================================================== Examples include::util/conditional-examples-header.adoc[] * Toggle between `floating` and `tiling` layouts (order of args doesn't matter): + `aerospace layout floating tiling` * Toggle orientation (order of args doesn't matter): + `aerospace layout horizontal vertical` * Toggle between `tiles` and `accordion` layouts (order of args doesn't matter): + `aerospace layout tiles accordion` * Switch to `tiles` layout. Toggle the layout orientation if already in `tiles` layout: + `aerospace layout tiles horizontal vertical` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-apps.adoc ================================================ = aerospace-list-apps(1) include::util/man-attributes.adoc[] :manname: aerospace-list-apps // tag::purpose[] :manpurpose: Print the list of running applications that appears in the Dock and may have a user interface // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-apps [-h|--help] [--macos-native-hidden [no]] [--format ] [--count] [--json] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} The command is useful to inspect list of applications to compose filter for xref:guide.adoc#on-window-detected-callback[on-window-detected callback] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --macos-native-hidden [no]:: Filter results to only print hidden applications. `[no]` inverts the condition --format :: Specify output format. See "Output Format" section for more details. Incompatible with `--count` --count:: Output only the number of apps. Incompatible with: `--format`, `--json` --json:: Output in JSON format. Can be used in combination with `--format` to specify which data to include into the json. Incompatible with `--count` // =========================================================== Output Format include::util/conditional-output-format-header.adoc[] Output format can be configured with optional `[--format ]` option. `` supports https://en.wikipedia.org/wiki/String_interpolation[string interpolation]. If not specified, the default `` is: + `%{app-pid}%{right-padding} | %{app-bundle-id}%{right-padding} | %{app-name}` The following variables can be used inside ``: %{app-bundle-id}:: String. Application unique identifier. https://developer.apple.com/documentation/appstoreconnectapi/bundle_ids[Bundle ID] %{app-name}:: String. Application name %{app-pid}:: Number. https://en.wikipedia.org/wiki/Process_identifier[UNIX process identifier] %{app-exec-path}:: String. Application executable path %{app-bundle-path}:: String. Application bundle path %{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column %{newline}:: Unicode U+000A newline symbol `\n` %{tab}:: Unicode U+0009 tab symbol `\t` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-exec-env-vars.adoc ================================================ = aerospace-list-exec-env-vars(1) include::util/man-attributes.adoc[] :manname: aerospace-list-exec-env-vars // tag::purpose[] :manpurpose: List environment variables that exec-* commands and callbacks are run with // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-exec-env-vars [-h|--help] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Examples of commands and callbacks: * `aerospace exec-and-forget` command * `exec-on-workspace-change-callback` // end::body[] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-modes.adoc ================================================ = aerospace-list-modes(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Print a list of modes currently specified in the configuration // end::purpose[] :manname: aerospace-list-modes // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-modes [-h|--help] [--current] [--count] [--json] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} See xref:guide.adoc#binding-modes[the guide] for documentation about binding modes // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --current:: Only print the currently active mode. Incompatible with `--count` --count:: Output only the number of modes. Incompatible with `--current`, `--json` --json:: Output in JSON format. Incompatible with `--count` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-monitors.adoc ================================================ = aerospace-list-monitors(1) include::util/man-attributes.adoc[] :manname: aerospace-list-monitors // tag::purpose[] :manpurpose: Print monitors that satisfy conditions // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-monitors [-h|--help] [--focused [no]] [--mouse [no]] [--format ] [--count] [--json] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --focused [no]:: Filter results to only print the focused monitor. `[no]` inverts the condition --mouse [no]:: Filter results to only print the monitor with the mouse. `[no]` inverts the condition --format :: Specify output format. See "Output Format" section for more details. Incompatible with `--count` --count:: Output only the number of monitors. Incompatible with `--format` --json:: Output in JSON format. Can be used in combination with `--format` to specify which data to include into the json. Incompatible with `--count` // =========================================================== Output Format include::util/conditional-output-format-header.adoc[] Output format can be configured with optional `[--format ]` option. `` supports https://en.wikipedia.org/wiki/String_interpolation[string interpolation]. If not specified, the default `` is: + `%{monitor-id}%{right-padding} | %{monitor-name}` The following variables can be used inside ``: %{monitor-id}:: 1-based Number. Sequential number of the belonging monitor %{monitor-appkit-nsscreen-screens-id}:: 1-based index of the belonging monitor in `NSScreen.screens` array. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar). %{monitor-name}:: String. Name of the belonging monitor %{monitor-is-main}:: Boolean. True if the monitor is main. %{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column %{newline}:: Unicode U+000A newline symbol `\n` %{tab}:: Unicode U+0009 tab symbol `\t` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-windows.adoc ================================================ = aerospace-list-windows(1) include::util/man-attributes.adoc[] :manname: aerospace-list-windows // tag::purpose[] :manpurpose: Print windows that satisfy conditions // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-windows [-h|--help] (--workspace ...|--monitor ...) [--monitor ...] [--workspace ...] [--pid ] [--app-bundle-id ] [--format ] [--count] [--json] aerospace list-windows [-h|--help] --all [--format ] [--count] [--json] aerospace list-windows [-h|--help] --focused [--format ] [--count] [--json] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help include::util/all-monitors-option.adoc[] --focused:: Print the focused window. Please note that it is possible for no window to be in focus. In that case, error is reported. --workspace ...:: Filter results to only print windows that belong to either of specified workspaces. `...` is a space-separated list of workspace names. + Possible values: + + . Workspace name . `focused` is a special workspace name that represents the focused workspace . `visible` is a special workspace name that represents all currently visible workspaces (In multi-monitor setup, there are multiple visible workspaces) include::util/monitor-option.adoc[] --pid :: Filter results to only print windows that belong to the Application with specified `` --app-bundle-id :: Filter results to only print windows that belong to the Application with specified https://developer.apple.com/documentation/appstoreconnectapi/bundle_ids[Bundle ID] + Deprecated (but still supported) flag name: `--app-id` --format :: Specify output format. See "Output Format" section for more details. Incompatible with `--count` --count:: Output only the number of windows. Incompatible with `--format` --json:: Output in JSON format. Can be used in combination with `--format` to specify which data to include into the json. Incompatible with `--count` // =========================================================== Output Format include::util/conditional-output-format-header.adoc[] Output format can be configured with optional `[--format ]` option. `` supports https://en.wikipedia.org/wiki/String_interpolation[string interpolation]. If not specified, the default `` is: + `%{window-id}%{right-padding} | %{app-name}%{right-padding} | %{window-title}` The following variables can be used inside ``: %{window-id}:: Number. Window unique ID %{window-title}:: String. Window title %{window-is-fullscreen}:: Boolean. Is window in fullscreen by `aerospace fullscreen` command %{window-layout}:: String. Alias for `%{window-parent-container-layout}` %{window-parent-container-layout}:: String. The layout (`v_tiles`, `h_tiles`, `v_accordion`, `h_accordion`, `floating`) of the window's parent container. %{app-bundle-id}:: String. Application unique identifier. https://developer.apple.com/documentation/appstoreconnectapi/bundle_ids[Bundle ID] %{app-name}:: String. Application name %{app-pid}:: Number. https://en.wikipedia.org/wiki/Process_identifier[UNIX process identifier] %{app-exec-path}:: String. Application executable path %{app-bundle-path}:: String. Application bundle path %{workspace}:: String. Name of the belonging workspace %{workspace-is-focused}:: Boolean. True if the workspace has focus %{workspace-is-visible}:: Boolean. True if the workspace is visible. A workspace can be visible but not focused in a multi-monitor setup %{workspace-root-container-layout}:: String. The layout (`v_tiles`, `h_tiles`, `v_accordion`, `h_accordion`) of the workspace the window belongs to. %{monitor-id}:: 1-based Number. Sequential number of the belonging monitor. %{monitor-appkit-nsscreen-screens-id}:: 1-based index of the belonging monitor in `NSScreen.screens` array. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar). %{monitor-name}:: String. Name of the belonging monitor %{monitor-is-main}:: Boolean. True if the monitor is main. %{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column %{newline}:: Unicode U+000A newline symbol `\n` %{tab}:: Unicode U+0009 tab symbol `\t` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-list-workspaces.adoc ================================================ = aerospace-list-workspaces(1) include::util/man-attributes.adoc[] :manname: aerospace-list-workspaces // tag::purpose[] :manpurpose: Print workspaces that satisfy conditions // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace list-workspaces [-h|--help] --monitor ... [--visible [no]] [--empty [no]] [--format ] [--count] [--json] aerospace list-workspaces [-h|--help] --all [--format ] [--count] [--json] aerospace list-workspaces [-h|--help] --focused [--format ] [--count] [--json] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --format :: Specify output format. See "Output Format" section for more details include::util/all-monitors-option.adoc[] --focused:: Alias for `--monitor focused --visible`. Always prints a single workspace include::util/monitor-option.adoc[] --visible [no]:: Filter results to only print currently visible workspaces. `[no]` inverts the condition. Several workspaces can be visible in multi-monitor setup --empty [no]:: Filter results to only print empty workspaces. `[no]` inverts the condition. --format :: Specify output format. See "Output Format" section for more details. Incompatible with `--count` --count:: Output only the number of workspaces. Incompatible with `--format` --json:: Output in JSON format. Can be used in combination with `--format` to specify which data to include into the json. Incompatible with `--count` // =========================================================== Output Format include::util/conditional-output-format-header.adoc[] Output format can be configured with optional `[--format ]` option. `` supports https://en.wikipedia.org/wiki/String_interpolation[string interpolation]. If not specified, the default `` is: + `%{workspace}` The following variables can be used inside ``: %{workspace}:: String. Name of the belonging workspace %{workspace-is-focused}:: Boolean. True if the workspace has focus %{workspace-is-visible}:: Boolean. True if the workspace is visible. A workspace can be visible but not focused in a multi-monitor setup %{workspace-root-container-layout}:: String. The layout (`v_tiles`, `h_tiles`, `v_accordion`, `h_accordion`) of the workspace's root container %{monitor-id}:: 1-based Number. Sequential number of the belonging monitor %{monitor-appkit-nsscreen-screens-id}:: 1-based Number. Sequential number of the belonging monitor in `NSScreen.screens`. Useful for integration with other tools that might be using `NSScreen.screens` ordering (like sketchybar). %{monitor-name}:: String. Name of the belonging monitor %{monitor-is-main}:: Boolean. True if the monitor is main. %{right-padding}:: A special variable which expands with a minimum number of spaces required to form a right padding in the appropriate column %{newline}:: Unicode U+000A newline symbol `\n` %{tab}:: Unicode U+0009 tab symbol `\t` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-macos-native-fullscreen.adoc ================================================ = aerospace-macos-native-fullscreen(1) include::util/man-attributes.adoc[] :manname: aerospace-macos-native-fullscreen // tag::purpose[] :manpurpose: Toggle macOS fullscreen for the focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace macos-native-fullscreen [-h|--help] [--window-id ] aerospace macos-native-fullscreen [-h|--help] [--window-id ] [--fail-if-noop] on aerospace macos-native-fullscreen [-h|--help] [--window-id ] [--fail-if-noop] off // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --fail-if-noop:: Exit with non-zero exit code if already fullscreen or already not fullscreen --window-id :: include::./util/window-id-flag-desc.adoc[] // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] on, off:: `on` means enter fullscreen mode. `off` means exit fullscreen mode. Toggle between the two if not specified // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-macos-native-minimize.adoc ================================================ = aerospace-macos-native-minimize(1) include::util/man-attributes.adoc[] :manname: aerospace-macos-native-minimize // tag::purpose[] :manpurpose: Minimize focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace macos-native-minimize [-h|--help] [--window-id ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-mode.adoc ================================================ = aerospace-mode(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Activate the specified binding mode // end::purpose[] :manname: aerospace-mode // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace mode [-h|--help] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} See xref:guide.adoc#binding-modes[the guide] for documentation about binding modes // end::body[] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-move-mouse.adoc ================================================ = aerospace-move-mouse(1) include::./util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Move mouse to the requested position // end::purpose[] :manname: aerospace-move-mouse // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace move-mouse [-h|--help] [--fail-if-noop] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --fail-if-noop:: Exit with non-zero exit code if mouse is already at the requested position. The flag is compatible only with `window-lazy-center` and `monitor-lazy-center` arguments. // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] :: Position to move mouse to. Possible values: + * `monitor-lazy-center`. Move mouse to the center of the focused monitor, *unless* it is already within the monitor boundaries. * `monitor-force-center`. Move mouse to the center of the focused monitor. * `window-lazy-center`. Move mouse to the center of the focused window, *unless* it is already within the window boundaries. Exit with non-zero code if no window is focused. * `window-force-center`. Move mouse to the center of the focused window. Exit with non-zero code if no window is focused. // =========================================================== Examples include::util/conditional-examples-header.adoc[] * Try to move mouse to the center of the window. If there is no window in focus, move mouse to the center of the monitor: + `aerospace move-mouse window-lazy-center || aerospace move-mouse monitor-lazy-center` // end::body[] // =========================================================== Footer include::./util/man-footer.adoc[] ================================================ FILE: docs/aerospace-move-node-to-monitor.adoc ================================================ = aerospace-move-node-to-monitor(1) include::util/man-attributes.adoc[] :manname: aerospace-move-node-to-monitor // tag::purpose[] :manpurpose: Move window to monitor targeted by relative direction, by order, or by pattern // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace move-node-to-monitor [-h|--help] [--window-id ] [--focus-follows-window] [--wrap-around] (left|down|up|right|next|prev) aerospace move-node-to-monitor [-h|--help] [--window-id ] [--focus-follows-window] [--fail-if-noop] ... // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --wrap-around:: Make it possible to wrap around the movement --focus-follows-window:: Make sure that the window in question receives focus after moving. This flag is a shortcut for manually running `aerospace-workspace`/`aerospace-focus` after `move-node-to-monitor` successful execution. --fail-if-noop:: Exit with non-zero code if moving window to monitor it already belongs to --window-id :: include::./util/window-id-flag-desc.adoc[] // =========================================================== Arguments include::util/conditional-arguments-header.adoc[] (left|down|up|right):: Move window to monitor in direction relative to the focused monitor (next|prev):: Move window to next|prev monitor in order they appear in tray icon ...:: Find the first matching monitor and move the window there. Multiple monitor patterns is useful for different monitor configurations. Monitor patterns follow the same format as in `workspace-to-monitor-force-assignment` config option // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-move-node-to-workspace.adoc ================================================ = aerospace-move-node-to-workspace(1) include::util/man-attributes.adoc[] :manname: aerospace-move-node-to-workspace // tag::purpose[] :manpurpose: Move the focused window to the specified workspace // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace move-node-to-workspace [-h|--help] [--focus-follows-window] [--wrap-around] [--stdin|--no-stdin] (next|prev) aerospace move-node-to-workspace [-h|--help] [--focus-follows-window] [--fail-if-noop] [--window-id ] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} `(next|prev)` is identical to `workspace (next|prev)` // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --wrap-around:: Make it possible to jump between first and last workspaces using (next|prev) --fail-if-noop:: Exit with non-zero code if move window to workspace it already belongs to --focus-follows-window:: Make sure that the window in question receives focus after moving. This flag is a shortcut for manually running `aerospace-workspace`/`aerospace-focus` after `move-node-to-workspace` successful execution. --window-id :: include::./util/window-id-flag-desc.adoc[] --stdin:: Read the list of workspaces from stdin. Incompatible with `--no-stdin` --no-stdin:: Ignore the list of workspaces from stdin, even if provided. Incompatible with `--stdin` // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (next|prev):: Move window to next or prev workspace :: Specifies workspace name where to move window to // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-move-workspace-to-monitor.adoc ================================================ = aerospace-move-workspace-to-monitor(1) include::util/man-attributes.adoc[] :manname: aerospace-move-workspace-to-monitor // tag::purpose[] :manpurpose: Move workspace to monitor targeted by relative direction, by order, or by pattern. // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace move-workspace-to-monitor [-h|--help] [--workspace ] [--wrap-around] (left|down|up|right) aerospace move-workspace-to-monitor [-h|--help] [--workspace ] [--wrap-around] (next|prev) aerospace move-workspace-to-monitor [-h|--help] [--workspace ] ... // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Focus follows the focused workspace, so the workspace stays focused. The command fails for workspaces xref:guide.adoc#assign-workspaces-to-monitors[that have monitor force assignment]. // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --wrap-around:: Allows to move workspace between first and last monitors --workspace :: include::./util/workspace-flag-desc.adoc[] // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (left|down|up|right):: Move workspace to monitor in direction relative to the focused monitor (next|prev):: Move the workspace to next or prev monitor. 'next' or 'prev' monitor is calculated relative to the monitor `` currently belongs to. :: Find the first matching monitor and move the workspace there. Multiple monitor patterns is useful for different monitor configurations. Monitor patterns follow the same format as in `workspace-to-monitor-force-assignment` config option // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-move.adoc ================================================ = aerospace-move(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Move the focused window in the given direction // end::purpose[] :manname: aerospace-move // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace move [-h|--help] [--window-id ] [--boundaries ] [--boundaries-action ] (left|down|up|right) // end::synopsis[] // =========================================================== Description == Description // tag::body[] Move the focused window in the given direction. See the "Examples" section for more details. Deprecated name: `move-through` // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] --boundaries :: Defines move boundaries. + `` possible values: `(workspace|all-monitors-outer-frame)`. + The default is: `workspace` --boundaries-action :: Defines the behavior when requested to move across the ``. + `` possible values: `(stop|fail|create-implicit-container)`. + The default is: `create-implicit-container` // =========================================================== Examples include::util/conditional-examples-header.adoc[] . Given this layout + ---- h_tiles ├── window 1 (focused) └── window 2 ---- + `move right` will result in the following layout + ---- h_tiles ├── window 2 └── window 1 (focused) ---- . Given this layout + ---- h_tiles ├── window 1 ├── window 2 (focused) └── v_tiles ├── window 3 └── window 4 ---- + `move right` will result in the following layout + ---- h_tiles ├── window 1 └── v_tiles ├── window 3 ├── window 2 (focused) └── window 4 ---- . Given this layout + ---- h_tiles ├── window 1 └── v_tiles ├── window 3 ├── window 2 (focused) └── window 4 ---- + `move left` will result in the following layout + ---- h_tiles ├── window 1 ├── window 2 (focused) └── v_tiles ├── window 3 └── window 4 ---- . *Implicit container example* + In some cases, `move` needs to implicitly create a container to fulfill your command. + Given this layout + ---- h_tiles ├── window 1 ├── window 2 (focused) └── window 3 ---- + `move up` will result in the following layout + ---- v_tiles ├── window 2 (focused) └── h_tiles ├── window 1 └── window 3 ---- + `v_tiles` is an implicitly created container. + *Remark*: If `--boundaries` is set to `all-monitors-outer-frame` and there is a monitor in the `up` direction, the implicit container isn't created. + Instead, `window 2` would be moved to the monitor above the current. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-reload-config.adoc ================================================ = aerospace-reload-config(1) include::util/man-attributes.adoc[] :manname: aerospace-reload-config // tag::purpose[] :manpurpose: Reload currently active config // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace reload-config [-h|--help] [--no-gui] [--dry-run] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} If the config contains errors they will be printed to stdout, and GUI will open to show the errors. // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --no-gui:: Don't open GUI to show error. Only use stdout to report errors --dry-run:: Validate the config and show errors (if any) but don't reload the config include::util/conditional-exit-code-header.adoc[] 0:: Success. The config is reloaded successfully. non-zero exit code:: Failure. The config contains errors. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-resize.adoc ================================================ = aerospace-resize(1) include::util/man-attributes.adoc[] :manname: aerospace-resize // tag::purpose[] :manpurpose: Resize the focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace resize [-h|--help] [--window-id ] (smart|smart-opposite|width|height) [+|-] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} The dimension to resize is chosen by the first argument * `width` changes width * `height` changes height * `smart` changes width if the parent has horizontal orientation, and it changes height if the parent has vertical orientation * `smart-opposite` resizes the opposite axis of smart Second argument controls how much the size changes * If the `` is prefixed with `+` then the dimension is increased * If the `` is prefixed with `-` then the dimension is decreased * If the `` is prefixed with neither `+` nor `-` then the command changes the absolute value of the dimension // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-split.adoc ================================================ = aerospace-split(1) include::util/man-attributes.adoc[] :manname: aerospace-split // tag::purpose[] :manpurpose: Split focused window // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace split [-h|--help] [--window-id ] (horizontal|vertical|opposite) // end::synopsis[] // =========================================================== Description == Description // tag::body[] `split` command exists solely for compatibility with i3. Unless you're hardcore i3 user who knows what they are doing, it's recommended to use `join-with` *If the parent of focused window contains more than one child*, then the command . Creates a new tiling container . Replaces the focused window with the container . Puts the focused window into the container as its only child The argument configures orientation of the newly created container. `opposite` means opposite orientation compared to the parent container. *If the parent of the focused window contains only a single child* (the window itself), then `split` command changes the orientation of the parent container IMPORTANT: `split` command has no effect if `enable-normalization-flatten-containers` is turned on. Consider using `join-with` if you want to keep `enable-normalization-flatten-containers` enabled // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-subscribe.adoc ================================================ = aerospace-subscribe(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Subscribe to AeroSpace events and receive notifications via socket // end::purpose[] :manname: aerospace-subscribe // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace subscribe [-h|--help] [--all] [--no-send-initial] [...] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} This command connects to the AeroSpace server and receives real-time event notifications as JSON lines. The connection remains open until terminated (e.g., via Ctrl+C). On connect, the current state is sent immediately (focus-changed with current window/workspace, mode-changed with current mode, etc.). // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --all:: Subscribe to all event types --no-send-initial:: Do not send the initial state on connect // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] The following events can be subscribed to: focus-changed:: Fired when window focus changes. Includes `windowId`, `workspace`. focused-monitor-changed:: Fired when the focused monitor changes. Includes `workspace`, `monitorId`. focused-workspace-changed:: Fired when the focused workspace changes. Includes `workspace`, `prevWorkspace`. mode-changed:: Fired when the binding mode changes. Includes `mode`. window-detected:: Fired when a new window is detected. Includes `windowId`, `workspace`, `appBundleId`, `appName`. binding-triggered:: Fired when a keyboard binding is triggered. Includes `binding`, `mode`. // =========================================================== Output Format include::util/conditional-output-format-header.adoc[] Events are output as JSON lines (one JSON object per line): ---- {"_event":"focused-monitor-changed","monitorId":1,"workspace":"M"} {"_event":"focused-workspace-changed","prevWorkspace":"M","workspace":"M"} {"_event":"mode-changed","mode":"main"} {"_event":"focus-changed","windowId":28218,"workspace":"M"} ---- // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-summon-workspace.adoc ================================================ = aerospace-summon-workspace(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Move the requested workspace to the focused monitor. // end::purpose[] :manname: aerospace-summon-workspace // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace summon-workspace [-h|--help] [--fail-if-noop] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} The moved workspace becomes focused. The behavior is identical to Xmonad. The command makes sense only in multi-monitor setup. In single monitor setup the command is identical to `workspace` command. // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --fail-if-noop:: Exit with non-zero exit code if the workspace is already visible on the focused monitor. // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] :: The workspace to operate on. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-swap.adoc ================================================ = aerospace-swap(1) include::util/man-attributes.adoc[] :manname: aerospace-swap // tag::purpose[] :manpurpose: Swaps the focused window with another window. // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace swap [-h|--help] [--window-id ] [--swap-focus] [--wrap-around] (left|down|up|right|dfs-next|dfs-prev) // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} The operation is equivalent to dragging a window with the mouse. // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --window-id :: include::./util/window-id-flag-desc.adoc[] --swap-focus:: Swap focus away from the currently focused window. By default, this command does not change the focused window. --wrap-around:: Wrap around if the window is at the edge of the workspace (for `(left|down|up|right)`) or the start/end of the depth first order (for `(dfs-next|dfs-prev)`). // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (left|down|up|right):: Swaps the focused window with the nearest window in the given direction. (dfs-next|dfs-prev):: Swaps the focused window with the next or previous window in the depth-first order (top-to-bottom and left-to-right) of windows in the current workspace tree. // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-trigger-binding.adoc ================================================ = aerospace-trigger-binding(1) include::util/man-attributes.adoc[] :manname: aerospace-trigger-binding // tag::purpose[] :manpurpose: Trigger AeroSpace binding as if it was pressed by user // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace trigger-binding [-h|--help] --mode // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} You can use aerospace-config command to inspect available bindings: + `aerospace config --get mode.main.binding --keys` // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --mode :: Mode to search `` in // =========================================================== Arguments include::util/conditional-arguments-header.adoc[] :: Binding to trigger // =========================================================== Examples include::util/conditional-examples-header.adoc[] * Run alphabetically first binding from config (useless and synthetic example): + `aerospace trigger-binding --mode main "$(aerospace config --get mode.main.binding --keys | head -1)"` * Trigger `alt-tab` binding: + `aerospace trigger-binding --mode main alt-tab` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-volume.adoc ================================================ = aerospace-volume(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Manipulate volume // end::purpose[] :manname: aerospace-volume // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace volume [-h|--help] (up|down) [--no-gui] aerospace volume [-h|--help] (mute-toggle|mute-off|mute-on) [--no-gui] aerospace volume [-h|--help] set [--no-gui] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} // =========================================================== Options include::./util/conditional-options-header.adoc[] -h, --help:: Print help --no-gui:: Don't show volume GUI indicator // =========================================================== Arguments include::./util/conditional-arguments-header.adoc[] (up|down):: Increase or decrease the volume (mute-toggle|mute-on|mute-off):: Toggle/On/Off mute set :: Set volume to the exact value on scale from 0 to 100 // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-workspace-back-and-forth.adoc ================================================ = aerospace-workspace-back-and-forth(1) include::util/man-attributes.adoc[] // tag::purpose[] :manpurpose: Switch between the focused workspace and previously focused workspace back and forth // end::purpose[] :manname: aerospace-workspace-back-and-forth // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace workspace-back-and-forth [-h|--help] // end::synopsis[] // =========================================================== Description == Description // tag::body[] {manpurpose} Unlike `focus-back-and-forth`, `workspace-back-and-forth` always succeeds. Because unlike windows, workspaces can not be "closed". Workspaces are name-addressable objects. They are created and destroyed on the fly. Also see: <> // end::body[] // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace-workspace.adoc ================================================ = aerospace-workspace(1) include::util/man-attributes.adoc[] :manname: aerospace-workspace // tag::purpose[] :manpurpose: Focus the specified workspace // end::purpose[] // =========================================================== Synopsis == Synopsis [verse] // tag::synopsis[] aerospace workspace [-h|--help] [--auto-back-and-forth] [--fail-if-noop] aerospace workspace [-h|--help] [--wrap-around] [--stdin|--no-stdin] (next|prev) // end::synopsis[] // =========================================================== Description == Description // tag::body[] *1. syntax* {manpurpose} *2. (next|prev) syntax* Focuses next or previous workspace in *the list*. * If `--stdin` is specified, then *the list* is taken from stdin * Otherwise, *the list* is defined as all workspaces on focused monitor in alphabetical order // =========================================================== Options include::util/conditional-options-header.adoc[] -h, --help:: Print help --wrap-around:: Make it possible to jump between first and last workspaces using `(next|prev)` --auto-back-and-forth:: Automatic `back-and-forth` when switching to already focused workspace. Incompatible with `--fail-if-noop` --fail-if-noop:: Exit with non-zero exit code if switch to the already focused workspace. Incompatible with `--auto-back-and-forth` --stdin:: Read the list of workspaces from stdin. Incompatible with `--no-stdin` --no-stdin:: Ignore the list of workspaces from stdin, even if provided. Incompatible with `--stdin` // =========================================================== Examples include::util/conditional-examples-header.adoc[] * Go to the next non empty workspace on the focused monitor: + `aerospace list-workspaces --monitor focused --empty no | aerospace workspace --stdin next` // end::body[] // =========================================================== Footer include::util/man-footer.adoc[] ================================================ FILE: docs/aerospace.adoc ================================================ = aerospace(1) include::util/man-attributes.adoc[] :manname: aerospace :manpurpose: i3-like tiling window manager for macOS == Synopsis [verse] aerospace [-h|--help] [-v|--version] [...] [...] == Description AeroSpace is an i3-like tiling window manager for macOS *aerospace* command line program is used to manipulate AeroSpace and query its state. See https://nikitabobko.github.io/AeroSpace/commands for available options See each individual man page for and == Options -h, --help:: Print help include::util/man-footer.adoc[] ================================================ FILE: docs/commands.adoc ================================================ = AeroSpace Commands include::util/site-attributes.adoc[] include::util/header.adoc[] Commands documentation is also available as manpages. == balance-sizes ---- include::aerospace-balance-sizes.adoc[tags=synopsis] ---- include::aerospace-balance-sizes.adoc[tags=purpose] include::aerospace-balance-sizes.adoc[tags=body] == close ---- include::aerospace-close.adoc[tags=synopsis] ---- include::aerospace-close.adoc[tags=purpose] include::aerospace-close.adoc[tags=body] == close-all-windows-but-current ---- include::aerospace-close-all-windows-but-current.adoc[tags=synopsis] ---- include::aerospace-close-all-windows-but-current.adoc[tags=purpose] include::aerospace-close-all-windows-but-current.adoc[tags=body] == enable ---- include::aerospace-enable.adoc[tags=synopsis] ---- include::aerospace-enable.adoc[tags=purpose] include::aerospace-enable.adoc[tags=body] == exec-and-forget ---- include::aerospace-exec-and-forget.adoc[tags=synopsis] ---- include::aerospace-exec-and-forget.adoc[tags=purpose] include::aerospace-exec-and-forget.adoc[tags=body] == flatten-workspace-tree ---- include::aerospace-flatten-workspace-tree.adoc[tags=synopsis] ---- include::aerospace-flatten-workspace-tree.adoc[tags=purpose] include::aerospace-flatten-workspace-tree.adoc[tags=body] == focus ---- include::aerospace-focus.adoc[tags=synopsis] ---- include::aerospace-focus.adoc[tags=purpose] include::aerospace-focus.adoc[tags=body] == focus-back-and-forth ---- include::./aerospace-focus-back-and-forth.adoc[tags=synopsis] ---- include::./aerospace-focus-back-and-forth.adoc[tags=purpose] include::./aerospace-focus-back-and-forth.adoc[tags=body] == focus-monitor ---- include::aerospace-focus-monitor.adoc[tags=synopsis] ---- include::aerospace-focus-monitor.adoc[tags=purpose] include::aerospace-focus-monitor.adoc[tags=body] == fullscreen ---- include::aerospace-fullscreen.adoc[tags=synopsis] ---- include::aerospace-fullscreen.adoc[tags=purpose] include::aerospace-fullscreen.adoc[tags=body] == join-with ---- include::aerospace-join-with.adoc[tags=synopsis] ---- include::aerospace-join-with.adoc[tags=purpose] include::aerospace-join-with.adoc[tags=body] == layout ---- include::aerospace-layout.adoc[tags=synopsis] ---- include::aerospace-layout.adoc[tags=purpose] include::aerospace-layout.adoc[tags=body] == macos-native-fullscreen ---- include::aerospace-macos-native-fullscreen.adoc[tags=synopsis] ---- include::aerospace-macos-native-fullscreen.adoc[tags=purpose] include::aerospace-macos-native-fullscreen.adoc[tags=body] == macos-native-minimize ---- include::aerospace-macos-native-minimize.adoc[tags=synopsis] ---- include::aerospace-macos-native-minimize.adoc[tags=purpose] include::aerospace-macos-native-minimize.adoc[tags=body] == mode ---- include::aerospace-mode.adoc[tags=synopsis] ---- include::aerospace-mode.adoc[tags=purpose] include::aerospace-mode.adoc[tags=body] [#move] == move ---- include::aerospace-move.adoc[tags=synopsis] ---- include::aerospace-move.adoc[tags=purpose] include::aerospace-move.adoc[tags=body] == move-mouse ---- include::./aerospace-move-mouse.adoc[tags=synopsis] ---- include::./aerospace-move-mouse.adoc[tags=purpose] include::./aerospace-move-mouse.adoc[tags=body] == move-node-to-monitor ---- include::aerospace-move-node-to-monitor.adoc[tags=synopsis] ---- include::aerospace-move-node-to-monitor.adoc[tags=purpose] include::aerospace-move-node-to-monitor.adoc[tags=body] == move-node-to-workspace ---- include::aerospace-move-node-to-workspace.adoc[tags=synopsis] ---- include::aerospace-move-node-to-workspace.adoc[tags=purpose] include::aerospace-move-node-to-workspace.adoc[tags=body] == move-workspace-to-monitor ---- include::aerospace-move-workspace-to-monitor.adoc[tags=synopsis] ---- include::aerospace-move-workspace-to-monitor.adoc[tags=purpose] include::aerospace-move-workspace-to-monitor.adoc[tags=body] == reload-config ---- include::aerospace-reload-config.adoc[tags=synopsis] ---- include::aerospace-reload-config.adoc[tags=purpose] include::aerospace-reload-config.adoc[tags=body] == resize ---- include::aerospace-resize.adoc[tags=synopsis] ---- include::aerospace-resize.adoc[tags=purpose] include::aerospace-resize.adoc[tags=body] == split ---- include::aerospace-split.adoc[tags=synopsis] ---- include::aerospace-split.adoc[tags=purpose] include::aerospace-split.adoc[tags=body] == swap ---- include::aerospace-swap.adoc[tags=synopsis] ---- include::aerospace-swap.adoc[tags=purpose] include::aerospace-swap.adoc[tags=body] == summon-workspace ---- include::./aerospace-summon-workspace.adoc[tags=synopsis] ---- include::./aerospace-summon-workspace.adoc[tags=purpose] include::./aerospace-summon-workspace.adoc[tags=body] == trigger-binding ---- include::aerospace-trigger-binding.adoc[tags=synopsis] ---- include::aerospace-trigger-binding.adoc[tags=purpose] include::aerospace-trigger-binding.adoc[tags=body] == volume ---- include::./aerospace-volume.adoc[tags=synopsis] ---- include::./aerospace-volume.adoc[tags=purpose] include::./aerospace-volume.adoc[tags=body] == workspace ---- include::aerospace-workspace.adoc[tags=synopsis] ---- include::aerospace-workspace.adoc[tags=purpose] include::aerospace-workspace.adoc[tags=body] == workspace-back-and-forth ---- include::aerospace-workspace-back-and-forth.adoc[tags=synopsis] ---- include::aerospace-workspace-back-and-forth.adoc[tags=purpose] include::aerospace-workspace-back-and-forth.adoc[tags=body] == Query commands Query commands are commands that do not change the state but rather allow the examination of the current state. - Query commands are *NOT* available in config + (because there is no way to consume the stdout of these commands in config) - Query commands are only available in CLI === config ---- include::aerospace-config.adoc[tags=synopsis] ---- include::aerospace-config.adoc[tags=purpose] include::aerospace-config.adoc[tags=body] === debug-windows ---- include::aerospace-debug-windows.adoc[tags=synopsis] ---- include::aerospace-debug-windows.adoc[tags=purpose] include::aerospace-debug-windows.adoc[tags=body] === list-apps ---- include::aerospace-list-apps.adoc[tags=synopsis] ---- include::aerospace-list-apps.adoc[tags=purpose] include::aerospace-list-apps.adoc[tags=body] === list-exec-env-vars ---- include::aerospace-list-exec-env-vars.adoc[tags=synopsis] ---- include::aerospace-list-exec-env-vars.adoc[tags=purpose] include::aerospace-list-exec-env-vars.adoc[tags=body] === list-modes ---- include::aerospace-list-modes.adoc[tags=synopsis] ---- include::aerospace-list-modes.adoc[tags=purpose] include::aerospace-list-modes.adoc[tags=body] === list-monitors ---- include::aerospace-list-monitors.adoc[tags=synopsis] ---- include::aerospace-list-monitors.adoc[tags=purpose] include::aerospace-list-monitors.adoc[tags=body] === list-windows ---- include::aerospace-list-windows.adoc[tags=synopsis] ---- include::aerospace-list-windows.adoc[tags=purpose] include::aerospace-list-windows.adoc[tags=body] === list-workspaces ---- include::aerospace-list-workspaces.adoc[tags=synopsis] ---- include::aerospace-list-workspaces.adoc[tags=purpose] include::aerospace-list-workspaces.adoc[tags=body] === subscribe ---- include::aerospace-subscribe.adoc[tags=synopsis] ---- include::aerospace-subscribe.adoc[tags=purpose] include::aerospace-subscribe.adoc[tags=body] ================================================ FILE: docs/config-examples/default-config.toml ================================================ # Place a copy of this config to ~/.aerospace.toml # After that, you can edit ~/.aerospace.toml to your liking # Config version for compatibility and deprecations # Fallback value (if you omit the key): config-version = 1 config-version = 2 # You can use it to add commands that run after AeroSpace startup. # Available commands : https://nikitabobko.github.io/AeroSpace/commands after-startup-command = [] # Start AeroSpace at login start-at-login = false # Automatically reload the config when the config file is saved # After setting this to true, reload once manually to start the auto-reloading auto-reload-config = false # Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization enable-normalization-flatten-containers = true enable-normalization-opposite-orientation-for-nested-containers = true # See: https://nikitabobko.github.io/AeroSpace/guide#layouts # The 'accordion-padding' specifies the size of accordion padding # You can set 0 to disable the padding feature accordion-padding = 30 # Possible values: tiles|accordion default-root-container-layout = 'tiles' # Possible values: horizontal|vertical|auto # 'auto' means: wide monitor (anything wider than high) gets horizontal orientation, # tall monitor (anything higher than wide) gets vertical orientation default-root-container-orientation = 'auto' # Mouse follows focus when focused monitor changes # Drop it from your config, if you don't like this behavior # See https://nikitabobko.github.io/AeroSpace/guide#on-focus-changed-callbacks # See https://nikitabobko.github.io/AeroSpace/commands#move-mouse # Fallback value (if you omit the key): on-focused-monitor-changed = [] on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] # You can effectively turn off macOS "Hide application" (cmd-h) feature by toggling this flag # Useful if you don't use this macOS feature, but accidentally hit cmd-h or cmd-alt-h key # Also see: https://nikitabobko.github.io/AeroSpace/goodies#disable-hide-app automatically-unhide-macos-hidden-apps = false # List of workspaces that should stay alive even when they contain no windows, # even when they are invisible. # This config option is only available since 'config-version = 2' # Fallback value (if you omit the key): persistent-workspaces = [] persistent-workspaces = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "I", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] # A callback that runs every time binding mode changes # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes # See: https://nikitabobko.github.io/AeroSpace/commands#mode on-mode-changed = [] # Possible values: (qwerty|dvorak|colemak) # See https://nikitabobko.github.io/AeroSpace/guide#key-mapping [key-mapping] preset = 'qwerty' # Gaps between windows (inner-*) and between monitor edges (outer-*). # Possible values: # - Constant: gaps.outer.top = 8 # - Per monitor: gaps.outer.top = [{ monitor.main = 16 }, { monitor."some-pattern" = 32 }, 24] # In this example, 24 is a default value when there is no match. # Monitor pattern is the same as for 'workspace-to-monitor-force-assignment'. # See: # https://nikitabobko.github.io/AeroSpace/guide#assign-workspaces-to-monitors [gaps] inner.horizontal = 0 inner.vertical = 0 outer.left = 0 outer.bottom = 0 outer.top = 0 outer.right = 0 # 'main' binding mode declaration # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes # 'main' binding mode must be always presented # Fallback value (if you omit the key): mode.main.binding = {} [mode.main.binding] # All possible keys: # - Letters. a, b, c, ..., z # - Numbers. 0, 1, 2, ..., 9 # - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9 # - F-keys. f1, f2, ..., f20 # - Special keys. minus, equal, period, comma, slash, backslash, quote, semicolon, # backtick, leftSquareBracket, rightSquareBracket, space, enter, esc, # backspace, tab, pageUp, pageDown, home, end, forwardDelete, # sectionSign (ISO keyboards only, european keyboards only) # - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual, # keypadMinus, keypadMultiply, keypadPlus # - Arrows. left, down, up, right # All possible modifiers: cmd, alt, ctrl, shift # All possible commands: https://nikitabobko.github.io/AeroSpace/commands # See: https://nikitabobko.github.io/AeroSpace/commands#exec-and-forget # You can uncomment the following lines to open up terminal with alt + enter shortcut # (like in i3) # alt-enter = '''exec-and-forget osascript -e ' # tell application "Terminal" # do script # activate # end tell' # ''' # See: https://nikitabobko.github.io/AeroSpace/commands#layout alt-slash = 'layout tiles horizontal vertical' alt-comma = 'layout accordion horizontal vertical' # See: https://nikitabobko.github.io/AeroSpace/commands#focus alt-h = 'focus left' alt-j = 'focus down' alt-k = 'focus up' alt-l = 'focus right' # See: https://nikitabobko.github.io/AeroSpace/commands#move alt-shift-h = 'move left' alt-shift-j = 'move down' alt-shift-k = 'move up' alt-shift-l = 'move right' # See: https://nikitabobko.github.io/AeroSpace/commands#resize alt-minus = 'resize smart -50' alt-equal = 'resize smart +50' # See: https://nikitabobko.github.io/AeroSpace/commands#workspace alt-1 = 'workspace 1' alt-2 = 'workspace 2' alt-3 = 'workspace 3' alt-4 = 'workspace 4' alt-5 = 'workspace 5' alt-6 = 'workspace 6' alt-7 = 'workspace 7' alt-8 = 'workspace 8' alt-9 = 'workspace 9' alt-a = 'workspace A' # In your config, you can drop workspace bindings that you don't need alt-b = 'workspace B' alt-c = 'workspace C' alt-d = 'workspace D' alt-e = 'workspace E' alt-f = 'workspace F' alt-g = 'workspace G' alt-i = 'workspace I' alt-m = 'workspace M' alt-n = 'workspace N' alt-o = 'workspace O' alt-p = 'workspace P' alt-q = 'workspace Q' alt-r = 'workspace R' alt-s = 'workspace S' alt-t = 'workspace T' alt-u = 'workspace U' alt-v = 'workspace V' alt-w = 'workspace W' alt-x = 'workspace X' alt-y = 'workspace Y' alt-z = 'workspace Z' # See: https://nikitabobko.github.io/AeroSpace/commands#move-node-to-workspace alt-shift-1 = 'move-node-to-workspace 1' alt-shift-2 = 'move-node-to-workspace 2' alt-shift-3 = 'move-node-to-workspace 3' alt-shift-4 = 'move-node-to-workspace 4' alt-shift-5 = 'move-node-to-workspace 5' alt-shift-6 = 'move-node-to-workspace 6' alt-shift-7 = 'move-node-to-workspace 7' alt-shift-8 = 'move-node-to-workspace 8' alt-shift-9 = 'move-node-to-workspace 9' alt-shift-a = 'move-node-to-workspace A' alt-shift-b = 'move-node-to-workspace B' alt-shift-c = 'move-node-to-workspace C' alt-shift-d = 'move-node-to-workspace D' alt-shift-e = 'move-node-to-workspace E' alt-shift-f = 'move-node-to-workspace F' alt-shift-g = 'move-node-to-workspace G' alt-shift-i = 'move-node-to-workspace I' alt-shift-m = 'move-node-to-workspace M' alt-shift-n = 'move-node-to-workspace N' alt-shift-o = 'move-node-to-workspace O' alt-shift-p = 'move-node-to-workspace P' alt-shift-q = 'move-node-to-workspace Q' alt-shift-r = 'move-node-to-workspace R' alt-shift-s = 'move-node-to-workspace S' alt-shift-t = 'move-node-to-workspace T' alt-shift-u = 'move-node-to-workspace U' alt-shift-v = 'move-node-to-workspace V' alt-shift-w = 'move-node-to-workspace W' alt-shift-x = 'move-node-to-workspace X' alt-shift-y = 'move-node-to-workspace Y' alt-shift-z = 'move-node-to-workspace Z' # See: https://nikitabobko.github.io/AeroSpace/commands#workspace-back-and-forth alt-tab = 'workspace-back-and-forth' # See: https://nikitabobko.github.io/AeroSpace/commands#move-workspace-to-monitor alt-shift-tab = 'move-workspace-to-monitor --wrap-around next' # See: https://nikitabobko.github.io/AeroSpace/commands#mode alt-shift-semicolon = 'mode service' # 'service' binding mode declaration. # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes [mode.service.binding] esc = ['reload-config', 'mode main'] r = ['flatten-workspace-tree', 'mode main'] # reset layout f = ['layout floating tiling', 'mode main'] # Toggle between floating and tiling layout backspace = ['close-all-windows-but-current', 'mode main'] # sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2 #s = ['layout sticky tiling', 'mode main'] alt-shift-h = ['join-with left', 'mode main'] alt-shift-j = ['join-with down', 'mode main'] alt-shift-k = ['join-with up', 'mode main'] alt-shift-l = ['join-with right', 'mode main'] ================================================ FILE: docs/config-examples/i3-like-config-example.toml ================================================ # Reference: https://github.com/i3/i3/blob/next/etc/config config-version = 2 # In i3, all workspaces are phantom persistent-workspaces = [] # i3 doesn't have "normalizations" feature that why we disable them here. # But the feature is very helpful. # Normalizations eliminate all sorts of weird tree configurations that don't make sense. # Give normalizations a chance and enable them back. enable-normalization-flatten-containers = false enable-normalization-opposite-orientation-for-nested-containers = false # Mouse follows focus when focused monitor changes on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] [mode.main.binding] # See: https://nikitabobko.github.io/AeroSpace/goodies#open-a-new-window-with-applescript alt-enter = '''exec-and-forget osascript -e ' tell application "Terminal" do script activate end tell' ''' # i3 wraps focus by default alt-j = 'focus --boundaries-action wrap-around-the-workspace left' alt-k = 'focus --boundaries-action wrap-around-the-workspace down' alt-l = 'focus --boundaries-action wrap-around-the-workspace up' alt-semicolon = 'focus --boundaries-action wrap-around-the-workspace right' alt-shift-j = 'move left' alt-shift-k = 'move down' alt-shift-l = 'move up' alt-shift-semicolon = 'move right' # Consider using 'join-with' command as a 'split' replacement if you want to enable # normalizations alt-h = 'split horizontal' alt-v = 'split vertical' alt-f = 'fullscreen' alt-s = 'layout v_accordion' # 'layout stacking' in i3 alt-w = 'layout h_accordion' # 'layout tabbed' in i3 alt-e = 'layout tiles horizontal vertical' # 'layout toggle split' in i3 alt-shift-space = 'layout floating tiling' # 'floating toggle' in i3 # Not supported, because this command is redundant in AeroSpace mental model. # See: https://nikitabobko.github.io/AeroSpace/guide#floating-windows #alt-space = 'focus toggle_tiling_floating' # `focus parent`/`focus child` are not yet supported, and it's not clear whether they # should be supported at all https://github.com/nikitabobko/AeroSpace/issues/5 # alt-a = 'focus parent' alt-1 = 'workspace 1' alt-2 = 'workspace 2' alt-3 = 'workspace 3' alt-4 = 'workspace 4' alt-5 = 'workspace 5' alt-6 = 'workspace 6' alt-7 = 'workspace 7' alt-8 = 'workspace 8' alt-9 = 'workspace 9' alt-0 = 'workspace 10' alt-shift-1 = 'move-node-to-workspace 1' alt-shift-2 = 'move-node-to-workspace 2' alt-shift-3 = 'move-node-to-workspace 3' alt-shift-4 = 'move-node-to-workspace 4' alt-shift-5 = 'move-node-to-workspace 5' alt-shift-6 = 'move-node-to-workspace 6' alt-shift-7 = 'move-node-to-workspace 7' alt-shift-8 = 'move-node-to-workspace 8' alt-shift-9 = 'move-node-to-workspace 9' alt-shift-0 = 'move-node-to-workspace 10' alt-shift-c = 'reload-config' alt-r = 'mode resize' [mode.resize.binding] h = 'resize width -50' j = 'resize height +50' k = 'resize height -50' l = 'resize width +50' enter = 'mode main' esc = 'mode main' ================================================ FILE: docs/goodies.adoc ================================================ = AeroSpace Goodies include::util/site-attributes.adoc[] include::util/header.adoc[] Do you have a cool automatization, AeroSpace integration, or workflow? Feel free to open an issue or pull request to add it to this list! The source code of the page can be found in the `./docs` directory. [#move-by-dragging-any-part-of-the-window] == Move windows by dragging any part of the window [source,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) [#non-native-fullscreen-in-firefox] == Non-native fullscreen in Firefox Change the following settings in `about:config`: ---- // Disable macOS native fullscreen in Firefox full-screen-api.macos-native-full-screen = false // Disable fullscreen transition animations full-screen-api.transition-duration.enter = 0 0 full-screen-api.transition-duration.leave = 0 0 ---- `full-screen-api.ignore-widgets` is also an interesting config knob that might be of your preference. [#highlight-focused-windows-with-colored-borders] == Highlight focused windows with colored borders To highlight the focused window with colored border you can use link:https://github.com/FelixKratz/JankyBorders[JankyBorders]. You can also use `after-startup-command` to start JankyBorders together with AeroSpace [source,toml] ---- after-startup-command = [ # JankyBorders has a built-in detection of already running process, # so it won't be run twice on AeroSpace restart 'exec-and-forget borders active_color=0xffe1e3e4 inactive_color=0xff494d64 width=5.0' ] ---- [#raycast-extension] == AeroSpace Raycast extension There is a third party Raycast extension for AeroSpace. https://www.raycast.com/limonkufu/aerospace If you struggle remembering shortcuts, it's useful to search for commands until they become muscle memory. [#disable-open-animations] == Disable windows opening animations Observable in Google Chrome [source,bash] ---- defaults write -g NSAutomaticWindowAnimationsEnabled -bool false ---- [#use-trackpad-gestures-to-switch-workspaces] == Use trackpad gestures to switch workspaces The following commands focus next or previous workspaces on monitors where the mouse is located [source,bash] ---- aerospace workspace "$(aerospace list-workspaces --monitor mouse --visible)" && aerospace workspace next aerospace workspace "$(aerospace list-workspaces --monitor mouse --visible)" && aerospace workspace prev ---- Use the software of your choice to assign trackpad gestures to the respective commands. Here are a few third party options in alphabetical order: * https://github.com/acsandmann/aerospace-swipe * link:https://folivora.ai/[BetterTouchTool] can assign arbitrary commands to trackpad gestures. Beware that you might need to specify full path to aerospace executable https://community.folivora.ai/t/how-to-execute-terminal-command-to-switch-workspaces-with-aerospace/35914 * https://github.com/MediosZ/SwipeAeroSpace CAUTION: Make sure that you trust project authors before running the distributed binaries. Alternatively, inspect the source code, and build it yourself. [#show-aerospace-workspaces-in-sketchybar] == Show AeroSpace workspaces in Sketchybar You can integrate AeroSpace workspace indicators with link:https://github.com/FelixKratz/SketchyBar[Sketchybar]. Use these snippets as a starting point. .~/.aerospace.toml [source,toml] ---- # Run Sketchybar together with AeroSpace # sketchybar has a built-in detection of already running process, # so it won't be run twice on AeroSpace restart after-startup-command = ['exec-and-forget sketchybar'] # Notify Sketchybar about workspace change exec-on-workspace-change = ['/bin/bash', '-c', 'sketchybar --trigger aerospace_workspace_change FOCUSED_WORKSPACE=$AEROSPACE_FOCUSED_WORKSPACE' ] ---- .~/.config/sketchybar/sketchybarrc [source,bash] ---- sketchybar --add event aerospace_workspace_change for sid in $(aerospace list-workspaces --all); do sketchybar --add item space.$sid left \ --subscribe space.$sid aerospace_workspace_change \ --set space.$sid \ background.color=0x44ffffff \ background.corner_radius=5 \ background.height=20 \ background.drawing=off \ label="$sid" \ click_script="aerospace workspace $sid" \ script="$CONFIG_DIR/plugins/aerospace.sh $sid" done ---- .~/.config/sketchybar/plugins/aerospace.sh [source,bash] ---- #!/usr/bin/env bash # make sure it's executable with: # chmod +x ~/.config/sketchybar/plugins/aerospace.sh if [ "$1" = "$FOCUSED_WORKSPACE" ]; then sketchybar --set $NAME background.drawing=on else sketchybar --set $NAME background.drawing=off fi ---- [#show-aerospace-workspaces-in-simple-bar] == Show AeroSpace workspaces in simple-bar link:https://github.com/Jean-Tinland/simple-bar[simple-bar] can be used to display AeroSpace workspaces. In order to sync simple-bar with AeroSpace, add these lines to your AeroSpace config file: .~/.aerospace.toml [source,toml] ---- # Notify simple-bar about window focus change on-focus-changed = [ "exec-and-forget osascript -e 'tell application id \"tracesOf.Uebersicht\" to refresh widget id \"simple-bar-index-jsx\"'", ] # Notify simple-bar about workspace change exec-on-workspace-change = [ '/bin/zsh', '-c', '/usr/bin/osascript -e "tell application id \"tracesOf.Uebersicht\" to refresh widget id \"simple-bar-index-jsx\""', ] ---- [#open-a-new-window-with-applescript] == Open a new window with AppleScript Invoking Safari/Terminal with a command the obvious way (`exec-and-forget open -a Safari`) results in an outcome that is probably not the intended one. Namely, that any workspace already containing an instance of Safari/Terminal is brought in focus. Opening *a new window* of a program that can support multiple windows (such as Safari or Terminal.app) can be accomplished with an AppleScript inlined in `aerospace.toml` as follows: - Safari + [source,toml] ---- ctrl-g = '''exec-and-forget osascript -e ' tell application "Safari" make new document at end of documents activate end tell' ''' ---- - Terminal + [source,toml] ---- ctrl-g = '''exec-and-forget osascript -e ' tell application "Terminal" do script activate end tell' ''' ---- [#disable-hide-app] == Disable annoying and useless "hide application" shortcut If `automatically-unhide-macos-hidden-apps` isn't enough, you can disable `cmd-h` altogether (which will make this hotkey unavailable for apps that might use it for other purposes) .~/.aerospace.toml [source,toml] ---- [mode.main.binding] cmd-h = [] # Disable "hide application" cmd-alt-h = [] # Disable "hide others" ---- [#screenshoot-shortcut] == Take screenshots to clipboard using keyboard shortcut You can configure a custom shortcut to take a screenshot. `screencapture` is a built-in macOS command. .~/.aerospace.toml [source,toml] ---- alt-shift-s = 'exec-and-forget screencapture -i -c' ---- [#i3-like-config] == i3 like config link:config-examples/i3-like-config-example.toml[Download i3-like-config.toml] [source,toml,subs="macros+,specialchars+"] ---- include::config-examples/i3-like-config-example.toml[] ---- [#spacelist] == Spacelist link:https://github.com/magicmark/spacelist[Spacelist] is a terminal UI application that provides a searchable interface to view all open windows organized by workspace. You can hit enter on a selected application to switch to that workspace. [#popular-apps-ids] == List of Apple application IDs The list is useful to compose custom xref:guide.adoc#on-window-detected-callback[on-window-detected callback]. [cols="1,3"] |=== |Application name|Application ID |Activity Monitor|`com.apple.ActivityMonitor` |AirPort Utility|`com.apple.airport.airportutility` |App Store|`com.apple.AppStore` |Audio MIDI Setup|`com.apple.audio.AudioMIDISetup` |Automator|`com.apple.Automator` |Books|`com.apple.iBooksX` |Calculator|`com.apple.calculator` |Calendar|`com.apple.iCal` |Chess|`com.apple.Chess` |Clock|`com.apple.clock` |ColorSync Utility|`com.apple.ColorSyncUtility` |Console|`com.apple.Console` |Contacts|`com.apple.AddressBook` |Dictionary|`com.apple.Dictionary` |Disk Utility|`com.apple.DiskUtility` |FaceTime|`com.apple.FaceTime` |Find My|`com.apple.findmy` |Finder|`com.apple.finder` |Freeform|`com.apple.freeform` |Grapher|`com.apple.grapher` |Home|`com.apple.Home` |iMovie|`com.apple.iMovieApp` |Keychain Access|`com.apple.keychainaccess` |Keynote|`com.apple.iWork.Keynote` |Mail|`com.apple.mail` |Maps|`com.apple.Maps` |Messages|`com.apple.MobileSMS` |Music|`com.apple.Music` |Notes|`com.apple.Notes` |Pages|`com.apple.iWork.Pages` |Photo Booth|`com.apple.PhotoBooth` |Photos|`com.apple.Photos` |Podcasts|`com.apple.podcasts` |Preview|`com.apple.Preview` |QuickTime Player|`com.apple.QuickTimePlayerX` |Reminders|`com.apple.reminders` |Safari|`com.apple.Safari` |Shortcuts|`com.apple.shortcuts` |Stocks|`com.apple.stocks` |System Settings|`com.apple.systempreferences` |Terminal|`com.apple.Terminal` |TextEdit|`com.apple.TextEdit` |Time Machine|`com.apple.backup.launcher` |TV|`com.apple.TV` |VoiceMemos|`com.apple.VoiceMemos` |VoiceOver Utility|`com.apple.VoiceOverUtility` |Weather|`com.apple.weather` |Xcode|`com.apple.dt.Xcode` |=== ================================================ FILE: docs/guide.adoc ================================================ = AeroSpace Guide include::util/site-attributes.adoc[] :url-search-for-config: https://github.com/search?q=path%3A*aerospace.toml&type=code include::util/header.adoc[] This Guide is designed to be read from top to bottom as a whole. You can skip parts that are obvious. [#installation] == Installation [#homebrew-installation] === Homebrew installation (Preferred) https://brew.sh/[Homebrew] is a package manager for macOS [source,bash] ---- brew install --cask nikitabobko/tap/aerospace ---- *(Optional)* You might need to configure your shell to enable completion provided by homebrew packages: https://docs.brew.sh/Shell-Completion AeroSpace provides bash, fish and zsh completions. You can also install specific previous https://github.com/nikitabobko/homebrew-tap/tree/main/Casks[pinned homebrew Casks versions]: [source,bash] ---- brew install --cask nikitabobko/tap/aerospace@0.12.0 ---- [#manual-installation] === Manual installation . Download the latest available zip from https://github.com/nikitabobko/AeroSpace/releases[releases page] . Unpack zip . Put unpacked `AeroSpace-v$VERSION/AeroSpace.app` to `/Applications` . Put unpacked `AeroSpace-v$VERSION/bin/aerospace` anywhere to `$PATH` (The step is optional. It is only needed if you want to be able to interact with AeroSpace from CLI) If you see this message ---- "AeroSpace.app" can't be opened because Apple cannot check it for malicious software. ---- **Option 1** to resolve the problem ``` xattr -d com.apple.quarantine /Applications/AeroSpace.app ``` **Option 2** to resolve the problem . navigate in Finder to `/Applications/AeroSpace.app` . Right mouse click . Open (yes, it's that stupid) [#configuring-aerospace] == Configuring AeroSpace [#config-location] === Custom config location AeroSpace tries to find the custom config in two locations: . `~/.aerospace.toml` . `+${XDG_CONFIG_HOME}/aerospace/aerospace.toml+` (environment variable `XDG_CONFIG_HOME` fallbacks to `~/.config` if the variable is not presented) If the config is found in more than one location then the ambiguity is reported. === Config samples Please see the following config samples: * <> * xref:./goodies.adoc#i3-like-config[i3 like config] * {url-search-for-config}[Search for configs by other users on GitHub] for inspiration AeroSpace uses TOML format for the config. TOML is easy to read, and it supports comments. See https://toml.io/en/v1.0.0[TOML spec for more info] [#default-config] === Default config The default config is part of the documentation, it contains all trivial configuration keys with comments. Please read the default config! Non-trivial configuration options are mentioned further in this guide. If no custom config is found, AeroSpace will load the default config. If the key is omitted in the custom config, it falls back to the value in the default config, unless it's stated otherwise for the specific keys. Namely: * `mode.*.binding`. It falls back to the empty TOML table. Your config is the source of truth for keyboard bindings. You must explicitly mention all the keyboard bindings and <> in your config. * `on-focused-monitor-changed`. It falls back to the empty TOML array. * `exec` TOML table. See: <> (It's so boring and verbose, I don't even want to mention it in the `default-config.toml`) Rule of thumb: all the "scalar like" values always fall back to the default config. All the "vector like" values fall back to the empty TOML array or table. That allows you to keep your config tidy and clean from trivial config keys for which you like the default values. You can bootstrap your custom config by copying the default config from the app installation - [source,shell] ---- cp /Applications/AeroSpace.app/Contents/Resources/default-config.toml ~/.aerospace.toml ---- link:config-examples/default-config.toml[Download default-config.toml] [source,toml,subs="macros+,specialchars+"] ---- include::config-examples/default-config.toml[] ---- [#binding-modes] === Binding modes You can create multiple sets of bindings by creating different binding modes. When you switch to a different binding mode, all the bindings from the current mode are deactivated, and only the bindings specified in the new mode become active. The initial binding mode that AeroSpace starts out with is "main". This feature is absolutely identical to the one https://i3wm.org/docs/userguide.html#binding_modes[in i3] Working with binding modes consists of two parts: 1. defining a binding to switch to the binding mode and 2. declaring the binding mode itself. [source,toml] ---- [mode.main.binding] # Declare 'main' binding mode alt-r = 'mode resize' # 1. Define a binding to switch to 'resize' mode [mode.resize.binding] # 2. Declare 'resize' binding mode minus = 'resize smart -50' equal = 'resize smart +50' ---- [#commands] === Commands Commands are the thing you use to manipulate AeroSpace and query its state. There are two ways on how you can use commands: . Bind keys to run AeroSpace commands. Example: + [source,toml] ---- [mode.main.binding] # Bind alt-1 key to switch to workspace 1 alt-1 = 'workspace 1' # Or bind a sequence of commands alt-shift-1 = ['move-node-to-workspace 1', 'workspace 1'] ---- . Run commands in CLI. Open up a Terminal.app and type: + [source,bash] ---- aerospace workspace 1 ---- For the list of available commands see: xref:commands.adoc[] [#key-mapping] === Keyboard layouts and key mapping By default, key bindings in the config are perceived as `qwerty` layout. If you use different layout, different alphabet, or you just want to have a fancy alias for the existing key, you can use `key-mapping.key-notation-to-key-code`. [source,toml] ---- # Define my fancy unicorn key notation [key-mapping.key-notation-to-key-code] unicorn = 'u' [mode.main.binding] alt-unicorn = 'workspace wonderland' # (⁀ᗢ⁀) ---- * For `dvorak` and `colemak` users, AeroSpace offers preconfigured presets. + [source,toml] ---- [key-mapping] preset = 'dvorak' # or 'colemak' ---- [#exec-env-vars] === exec-* Environment Variables You can configure environment variables of `exec-*` commands and callbacks (such as xref:commands.adoc#exec-and-forget[exec-and-forget], <>) * `exec.inherit-env-vars = true` configures whether inherit environment variables of `AeroSpace.app` or not. (The default is `true`) * You can override env variables with the following syntax: + [source,toml] ---- [exec.env-vars] PATH = '${HOME}/bin:${PATH}' ---- + Environment variable substitution is supported in form of `+${ENV_VAR}+` * You can inspect what is the end result of environment variables using xref:commands.adoc#list-exec-env-vars[`list-exec-env-vars` command] * GUI apps on macOS don’t have Homebrew’s prefix in their `PATH` by default (https://docs.brew.sh/FAQ#my-mac-apps-dont-find-homebrew-utilities[docs.brew.sh]). That's why unless you override `exec` section in your config, AeroSpace falls back to the following `exec` configuration: + [source,toml] ---- [exec] inherit-env-vars = true [exec.env-vars] PATH = '/opt/homebrew/bin:/opt/homebrew/sbin:${PATH}' ---- [#tree] == Tree AeroSpace stores all windows and containers in a tree. AeroSpace tree tiling model is https://i3wm.org/docs/userguide.html#tree[inspired by i3]. *Definition.* Each non-leaf node is called a "Container" WARNING: i3 has a different terminology. "container" in i3 is the same as "node" in AeroSpace. * Each workspace contains its own single root node * Each container can contain arbitrary number of children nodes * Windows are the only possible leaf nodes. Windows contain zero children nodes * Every container has two properties: . <> (Possible values: `tiles`, `accordion`) . Orientation (Possible values: `horizontal`, `vertical`) When we say "layout of the window", we refer to the layout of the window’s parent container. It’s easier to understand tree tiling model by looking at examples .Simple tree structure. Two windows side-by-side image::assets/h_tiles.png[] .Complex tree structure image::assets/tree.png[] You can nest containers as deeply as you want to. You can navigate in the tree in 4 possible cardinal directions (left, down, up, right). You use xref:commands.adoc#focus[focus command] to do that. The tree structure can be changed with three commands: . xref:commands.adoc#move[move] . xref:commands.adoc#join-with[join-with] . xref:commands.adoc#split[split] (it's for compatibility with i3. Please prefer `join-with` over `split`) [#layouts] === Layouts In total, AeroSpace provides 4 possible layouts: - `h_tiles` horizontal tiles (in i3, it’s called "horizontal split") - `v_tiles` vertical tiles (in i3, it’s called "vertical split") - `h_accordion` horizontal accordion (analog of i3’s "tabbed layout") - `v_accordion` vertical accordion (analog of i3’s "stacked layout") <>, you’re already familiar with the `tiles` layout. Accordion is a layout where windows are placed on top of each other. * *The horizontal accordion* shows left and right paddings to visually indicate the presence of other windows in those directions. * *The vertical accordion* shows top and bottom paddings to visually indicate the presence of other windows in those directions. .Horizontal accordion image::assets/h_accordion.png[,800,align="center"] .Vertical accordion image::assets/v_accordion.png[,800,align="center"] Just like in a `tiles` layout, you can use the xref:commands.adoc#focus[focus] command to navigate an accordion layout. You can navigate the windows in an `h_accordion` by using the `focus (left|right)` command. While in a `v_accordion`, you can navigate the windows using the `focus (up|down)` command. Accordion padding is configurable via `accordion-padding` option. [#normalization] === Normalization By default, AeroSpace does two types of tree normalizations: . Containers that have only one child are "flattened". The root container is an exception, it is allowed to have a single window child. Configured by `enable-normalization-flatten-containers` . Containers that nest into each other must have opposite orientations. Configured by `enable-normalization-opposite-orientation-for-nested-containers` [.lead] *Example 1* According to the first normalization, such layout isn’t possible: ---- h_tiles (root node) └── v_tiles └── window 1 ---- it will be immediately transformed into ---- v_tiles (new root node) └── window 1 ---- [.lead] *Example 2* According to the second normalization, such layout isn’t possible: ---- h_tiles ├── window 1 └── h_tiles ├── window 2 └── window 3 ---- it will be immediately transformed into ---- h_tiles ├── window 1 └── v_tiles ├── window 2 └── window 3 ---- Normalizations make it easier to understand the tree structure by looking at how windows are placed on the screen. Though you can disable normalizations by placing these lines to your config: [source,toml] ---- enable-normalization-flatten-containers = false enable-normalization-opposite-orientation-for-nested-containers = false ---- Unless you're hardcore i3 user who knows what they are doing, it's recommended to keep the normalizations enabled. [#floating-windows] === Floating windows Normally, floating windows are not considered to be part of the <>. But it’s not the case with xref:commands.adoc#focus[focus] command. From xref:commands.adoc#focus[focus] command perspective, floating windows are part of <>. The floating window parent container is determined as the smallest tiling container that contains the center of the floating window. This technique eliminates the need for an additional binding for focusing floating windows. [#emulation-of-virtual-workspaces] == Emulation of virtual workspaces Native macOS Spaces have a lot of problems * The animation for Spaces switching is slow ** You can’t disable animation for Spaces switching (you can only make it slightly faster by turning on `Reduce motion` setting, but it’s suboptimal) * You have a limit of Spaces (up to 16 Spaces with one monitor) * You can’t create/delete/reorder Space and move windows between Spaces with hotkeys (you can only switch between Spaces with hotkeys) * Apple doesn't provide public API to communicate with Spaces (create/delete/reorder/switch Space and move windows between Spaces) Since Spaces are so hard to deal with, AeroSpace reimplements Spaces and calls them "Workspaces". The idea is that if the workspace isn’t active then all of its windows are placed outside the visible area of the screen, in the bottom right or left corner. Once you switch back to the workspace, (e.g. by the means of xref:commands.adoc#workspace[workspace] command, or `cmd + tab`) windows are placed back to the visible area of the screen. When you quit the AeroSpace or when the AeroSpace detects that it's about to crash, AeroSpace will place all windows back to the visible area of the screen. AeroSpace shows the name of currently active workspace in its tray icon (top right corner), to give users a visual feedback on what workspace is currently active. The intended workflow of using AeroSpace workspaces is to only have one macOS Space (or as many monitors you have, if `Displays have separate Spaces` is enabled) and don’t interact with macOS Spaces anymore. [NOTE] ==== For better or worse, macOS doesn’t allow to place windows outside the visible area entirely. You will still be able to see a 1 pixel vertical line of "hidden" windows in the bottom right or left corner of your screen. That means, that if AeroSpace crashes badly you will still be able to manually "unhide" the windows by dragging these few pixels to the center of the screen. If you want to minimize the visibility of hidden windows, it's recommended to place Dock in the bottom (and additionally turn automatic hiding on) ==== === Proper monitor arrangement Since AeroSpace needs a free space to hide windows in, please make sure to arrange monitors in a way where *every monitor has free space in the bottom right or left corner.* (`System Settings -> Displays -> Arrange...`) If you fail to arrange your monitors properly, you will see parts of hidden windows on other monitors. .Bad monitor arrangement. Monitor 2 doesn't have free space in either of the bottom corners image::./assets/monitor-arrangement-1-bad.svg[,,align="center"] .Good monitor arrangement. Every monitor has free space in either of the bottom corners image::./assets/monitor-arrangement-1-good.svg[,,align="center"] .Bad monitor arrangement. Monitor 1 doesn't have free space in either of the bottom corners image::./assets/monitor-arrangement-2-bad.svg[,,align="center"] .Good monitor arrangement. Every monitor has free space in either of the bottom corners image::./assets/monitor-arrangement-2-good.svg[,,align="center"] [#a-note-on-mission-control] === A note on mission control For some reason, mission control doesn't like that AeroSpace puts a lot of windows in the bottom right corner of the screen. Mission control shows windows too small even when there is enough space to show them bigger. There is a workaround. You can enable `Group windows by application` setting: [source,bash] ---- defaults write com.apple.dock expose-group-apps -bool true && killall Dock ---- (or in System Settings: `System Settings -> Desktop & Dock -> Group windows by application`). For whatever weird reason, it helps. [#a-note-on-displays-have-separate-spaces] === A note on '`Displays have separate Spaces`' There is an observation that macOS works better and more stable if you disable `Displays have separate Spaces`. (It's enabled by default) People report all sorts of weird issues related to focus and performance when this setting is enabled: * Wrong window may receive focus in multi-monitor setup: https://github.com/nikitabobko/AeroSpace/issues/101[#101] (Bug in Apple API) * Wrong borderless Alacritty window may receive focus in *single monitor* setup: https://github.com/nikitabobko/AeroSpace/issues/247[#247] (Bug in Apple API) * Performance issues: https://github.com/nikitabobko/AeroSpace/issues/333[#333] * macOS randomly switches focus back: https://github.com/nikitabobko/AeroSpace/issues/289[#289] When `Displays have separate Spaces` is enabled, moving windows between monitors causes windows to move between different Spaces which is not correctly handled by the public APIs AeroSpace uses, apparently, these APIs are not aware about Spaces existence. Spaces are just cursed in macOS. The less Spaces you have, the better macOS behaves. |=== | |'`Displays have separate Spaces`' is enabled |'`Displays have separate Spaces`' is disabled |Is it possible for window to span across several monitors? |❌ No. macOS limitation |👍 Yes |Overall stability and performance |❌ Weird focus and performance issues may happen (see the list above) |👍 Public Apple API are more stable (which in turn affects AeroSpace stability) |When the first monitor is in fullscreen |👍 Second monitor operates independently |❌ Second monitor is unusable black screen |macOS status bar ... |... is displayed on both monitors |... is displayed only on main monitor |=== If you don't care about macOS native fullscreen in multi-monitor setup (which is itself clunky anyway, since it creates a separate Space instance), I recommend disabling `Displays have separate Spaces`. You can disable the setting by running: [source,bash] ---- defaults write com.apple.spaces spans-displays -bool true && killall SystemUIServer ---- (or in System Settings: `System Settings -> Desktop & Dock -> Displays have separate Spaces`). Logout is required for the setting to take effect. == Callbacks [#on-window-detected-callback] === 'on-window-detected' callback You can use `on-window-detected` callback to run commands every time a new window is detected. Here is a showcase example that uses all the possible configurations: [source,toml] ---- [[on-window-detected]] if.app-id = 'com.apple.systempreferences' if.app-name-regex-substring = 'settings' if.window-title-regex-substring = 'substring' if.workspace = 'workspace-name' if.during-aerospace-startup = true check-further-callbacks = true run = ['layout floating', 'move-node-to-workspace S'] # The callback itself ---- `run` commands are run only if the detected window matches all the specified conditions. If no conditions are specified then `run` is run every time a new window is detected. Several callbacks can be declared in the config. The callbacks are processed in the order they are declared. By default, the first callback that matches the criteria is run, and further callbacks are not considered. (The behavior can be overridden with `check-further-callbacks` option) Available window conditions are: [cols="1,2"] |=== |Condition TOML key |Condition description |`if.app-id` |Application ID exact match of the detected window |`if.app-name-regex-substring` |Application name case insensitive regex substring of the detected window |`if.window-title-regex-substring` |Window title case insensitive regex substring of the detected window |`if.during-aerospace-startup` a| * If `true` then run the callback only during AeroSpace startup. * If `false` then run callback only *NOT* during AeroSpace startup. * If not specified then the condition isn’t checked |`if.workspace` |Window's workspace name exact match |=== * `if.during-aerospace-startup = true` is useful if you want to do the initial app arrangement only on startup. * `if.during-aerospace-startup = false` is useful if you want to relaunch AeroSpace, but the callback has side effects that you don’t want to run on every relaunch. (e.g. the callback opens new windows) There are several ways to know `app-id`: * Take a look at precompiled xref:./goodies.adoc#popular-apps-ids[list of Apple application IDs] * You can use xref:commands.adoc#list-apps[`aerospace list-apps`] CLI command to get IDs of running applications * `mdls -name kMDItemCFBundleIdentifier -r /Applications/App.app` IMPORTANT: Some windows initialize their title after the window appears. `window-title-regex-substring` may not work as expected for such windows Examples of automations: * Assign apps on particular workspaces + [source,toml] ---- [[on-window-detected]] if.app-id = 'org.alacritty' run = 'move-node-to-workspace T' # mnemonics T - Terminal [[on-window-detected]] if.app-id = 'com.google.Chrome' run = 'move-node-to-workspace W' # mnemonics W - Web browser [[on-window-detected]] if.app-id = 'com.jetbrains.intellij' run = 'move-node-to-workspace I' # mnemonics I - IDE ---- * Make all windows float by default + [source,toml] ---- [[on-window-detected]] check-further-callbacks = true run = 'layout floating' ---- [#on-focus-changed-callbacks] === 'on-focus-changed' callbacks You can track focus changes using the following callbacks: `on-focus-changed` and `on-focused-monitor-changed`. * `on-focus-changed` is called every time focused window or workspace changes. * `on-focused-monitor-changed` is called every time focused monitor changes. A common use case for the callbacks is to implement "mouse follows focus" behavior. All you need is to combine the callback of your choice with xref:commands.adoc#move-mouse[move-mouse command]: [source,toml] ---- on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] # Mouse lazily follows focused monitor (default in i3) # or on-focus-changed = ['move-mouse window-lazy-center'] # Mouse lazily follows any focus (window or workspace) ---- You shouldn't rely on the order callbacks are called, since it's an implementation detail and can change from version to version. The callbacks are "recursion resistant", which means that any focus change within the callback won't retrigger the callback. Changing the focus within these callbacks is a bad idea anyway, and the way it's handled will probably change in future versions. [#exec-on-workspace-change-callback] === 'exec-on-workspace-change' callback `exec-on-workspace-change` callback allows to run arbitrary process when focused workspace changes. It may be useful for integrating with bars. [source,toml] ---- # Notify Sketchybar about workspace change exec-on-workspace-change = ['/bin/bash', '-c', 'sketchybar --trigger aerospace_workspace_change FOCUSED=$AEROSPACE_FOCUSED_WORKSPACE' ] ---- Besides the <>, the process has access to the following environment variables: * `AEROSPACE_FOCUSED_WORKSPACE` - the workspace user switched to * `AEROSPACE_PREV_WORKSPACE` - the workspace user switched from For a more elaborate example on how to integrate with Sketchybar see xref:./goodies.adoc#show-aerospace-workspaces-in-sketchybar[] [#multiple-monitors] == Multiple monitors * The pool of workspaces is shared between monitors * Each monitor shows its own workspace. The showed workspaces are called "visible" workspaces * Different monitors can’t show the same workspace at the same time * Each workspace (even invisible, even empty) has a monitor assigned to it * By default, all workspaces are assigned to the "main" monitor ("main" as in `System -> Displays -> Use as`) When you switch to a workspace: . AeroSpace takes the assigned monitor of the workspace and makes the workspace visible on the monitor . AeroSpace focuses the workspace You can move workspace to a different monitor with xref:commands.adoc#move-workspace-to-monitor[move-workspace-to-monitor] command. The idea of making pool of workspaces shared is based on [#observation]*the observation* that most users have a limited set of workspaces on their secondary monitors. Secondary monitors are frequently dedicated to specific tasks (browser, shell), or for monitoring various activities such as logs and dashboards. Thus, using one workspace per secondary monitor and "the rest" on the main monitor often makes sense. [NOTE] ==== The only difference between AeroSpace and i3 is switching to empty workspaces. When you switch to an empty workspace, AeroSpace puts the workspace on an assigned monitor; i3 puts the workspace on currently active monitor. * I find that AeroSpace model works better with <> listed above. * AeroSpace model is more consistent (it works the same for empty workspaces and non-empty workspaces) ==== [#assign-workspaces-to-monitors] === Assign workspaces to monitors You can use `workspace-to-monitor-force-assignment` syntax to assign workspaces to always appear on particular monitors [source,toml] ---- [workspace-to-monitor-force-assignment] 1 = 1 # Monitor sequence number from left to right. 1-based indexing 2 = 'main' # Main monitor 3 = 'secondary' # Non-main monitor in case when there are only two monitors 4 = 'built-in' # Case insensitive regex substring 5 = '^built-in retina display$' # Case insensitive regex match 6 = ['secondary', 'dell'] # You can specify multiple patterns. # The first matching pattern will be used ---- * Left hand side of the assignment is the workspace name * Right hand side of the assignment is the monitor pattern Supported monitor patterns: * `main` - "Main" monitor ("main" as in `System Settings -> Displays -> Use as`) * `secondary` - Non-main monitor in case when there are only two monitors * `` (e.g. `1`, `2`) - Sequence number of the monitor from left to right. 1-based indexing * `` (e.g. `+dell.*+`, `+built-in.*+`) - Case insensitive regex substring pattern You can specify multiple patterns as an array. The first matching pattern will be used xref:commands.adoc#move-workspace-to-monitor[move-workspace-to-monitor] command has no effect for workspaces that have monitor assignment == Dialog heuristics * Apple provides accessibility API for apps to let others know which of their windows are dialogs * A lot of apps don't implement this API or implement it improperly + Even some Apple dialogs don't implement the API properly. (E.g. Finder "Copy" progress window doesn't let others know that it's a dialog) AeroSpace uses the API to gently ask windows whether they are dialogs, but AeroSpace also applies some heuristics. For example, windows without a fullscreen button (NB! fullscreen button and maximize button are different buttons) are generally considered dialogs, excluding terminal apps (WezTerm, Alacritty, iTerm2, etc.). Windows that are recognized as dialogs are floated by default. If you find that some windows are not handled properly, you're welcome to create a PR that improves the heuristic. It's fine to hardcode special handling for popular applications, AeroSpace already does it. Please see `isDialogHeuristic` function in AeroSpace sources. You can also use `on-window-detected` to force tile or force float all windows of a particular application: . Force tile all the windows (or windows of a particular app) + [source,toml] ---- [[on-window-detected]] if.app-id = '...' run = 'layout tiling' ---- . Force float all the windows (or windows of a particular app) + [source,toml] ---- [[on-window-detected]] if.app-id = '...' run = 'layout floating' ---- == Common pitfall: keyboard keys handling If you can't make AeroSpace handle some keys in your config, please make sure that you don't have keys conflict with other software that might listen to global keys (e.g. skhd, Karabiner-Elements, Raycast) ================================================ FILE: docs/index.html ================================================ Redirect Redirect ================================================ FILE: docs/util/all-monitors-option.adoc ================================================ --all:: Alias for `--monitor all`. Please use this option *with caution*. Use it when you really need to get workspaces/windows from *all monitors*. + For multi-monitor setup `--monitor focused` is almost always a preferred option. If you're automating something then you don't want to mess up with workspaces/windows on a different monitor. + With great power comes great responsibility. ================================================ FILE: docs/util/conditional-arguments-header.adoc ================================================ ifndef::env-site[] == Arguments endif::[] ifdef::env-site[] [.lead] **ARGUMENTS** endif::[] ================================================ FILE: docs/util/conditional-examples-header.adoc ================================================ ifndef::env-site[] == Examples endif::[] ifdef::env-site[] [.lead] **EXAMPLES** endif::[] ================================================ FILE: docs/util/conditional-exit-code-header.adoc ================================================ ifndef::env-site[] == Exit code endif::[] ifdef::env-site[] [.lead] **EXIT CODE** endif::[] ================================================ FILE: docs/util/conditional-options-header.adoc ================================================ ifndef::env-site[] == Options endif::[] ifdef::env-site[] [.lead] **OPTIONS** endif::[] ================================================ FILE: docs/util/conditional-output-format-header.adoc ================================================ ifndef::env-site[] == Output Format endif::[] ifdef::env-site[] [.lead] **OUTPUT FORMAT** endif::[] ================================================ FILE: docs/util/header.adoc ================================================ ==== AeroSpace is an i3-like tiling window manager for macOS *Project homepage*: https://github.com/nikitabobko/AeroSpace image:assets/icon.png[300,300,float="right"] * xref:guide.adoc[AeroSpace Guide] * xref:commands.adoc[AeroSpace Commands] * xref:goodies.adoc[AeroSpace Goodies] ==== ================================================ FILE: docs/util/man-attributes.adoc ================================================ ifndef::env-site[] :doctype: manpage :manmanual: AeroSpace Manual :mansource: AeroSpace endif::[] ================================================ FILE: docs/util/man-footer.adoc ================================================ == Resources *Project homepage:* https://github.com/nikitabobko/AeroSpace + *Guide:* https://nikitabobko.github.io/AeroSpace/guide + == BUGS Bugs can be reported to https://github.com/nikitabobko/AeroSpace/discussions/categories/potential-bugs Maintainers will move verified bugs to https://github.com/nikitabobko/AeroSpace/issues == License Copyright (C) 2023 Nikita Bobko + Free use of this software is granted under the terms of the MIT License + You can find the full text of AeroSpace license and its dependencies in the 'legal' directory of the distributed zip archive. == AUTHOR Nikita Bobko and contributors ================================================ FILE: docs/util/monitor-option.adoc ================================================ --monitor :: Filter results to only print workspaces/windows that are attached to specified monitors. `` is a space separated list of monitor IDs. + + Possible monitors IDs: + + . 1-based index of a monitor as if monitors were ordered horizontally from left to right . `all` is a special monitor ID that represents all monitors . `mouse` is a special monitor ID that represents monitor with the mouse . `focused` is a special monitor ID that represents the focused monitor ================================================ FILE: docs/util/site-attributes.adoc ================================================ :idprefix: :idseparator: - :prewrap!: :relfilesuffix: :sectanchors: :sectlinks: :sectnums: :source-highlighter: pygments :toc: left :env-site: :favicon: ./assets/icon.png ================================================ FILE: docs/util/window-id-flag-desc.adoc ================================================ Act on the specified window instead of the focused window ================================================ FILE: docs/util/workspace-flag-desc.adoc ================================================ Act on the specified workspace instead of the focused workspace ================================================ FILE: format.sh ================================================ #!/usr/bin/env bash cd "$(dirname "$0")" source ./script/setup.sh check_uncommitted_files=0 while test $# -gt 0; do case $1 in --check-uncommitted-files) check_uncommitted_files=1; shift 1 ;; *) echo "Unknown option $1"; exit 1 ;; esac done if test $check_uncommitted_files -eq 1; then ./script/check-uncommitted-files.sh; fi ./script/install-dep.sh --swiftformat ./.deps/swiftformat/swiftformat . if test $check_uncommitted_files -eq 1; then ./script/check-uncommitted-files.sh; fi ./script/install-dep.sh --swiftlint ./.deps/swiftlint/swiftlint lint --quiet --fix ================================================ FILE: generate-shell-parser.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./script/install-dep.sh --antlr ./.deps/python-venv/bin/antlr4 -v "$antlr_version" -no-listener -Dlanguage=Swift \ -o ./ShellParserGenerated/Sources/ShellParserGenerated \ ./grammar/ShellLexer.g4 \ ./grammar/ShellParser.g4 mv ./ShellParserGenerated/Sources/ShellParserGenerated/grammar/*.swift ./ShellParserGenerated/Sources/ShellParserGenerated/ rm -rf ./ShellParserGenerated/Sources/ShellParserGenerated/grammar # Antlr generates weird *.interp and *.tokens files # Sources/ShellParserGenerated/ShellParser.swift:557:7: warning: variable '_prevctx' was written to, but never read # var _prevctx: CmdContext = _localctx sed -i '' '/_prevctx/d' ./ShellParserGenerated/Sources/ShellParserGenerated/ShellParser.swift ================================================ FILE: generate.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh export XCODEGEN_AEROSPACE_CODE_SIGN_IDENTITY="aerospace-codesign-certificate" build_version="0.0.0-SNAPSHOT" generate_xcodeproj=1 generate_cmd_help=1 generate_shell_parser=1 generate_git_hash=0 while test $# -gt 0; do case $1 in --build-version) build_version="$2"; shift 2 ;; --codesign-identity) XCODEGEN_AEROSPACE_CODE_SIGN_IDENTITY="$2"; shift 2 ;; --generate-git-hash) generate_git_hash=1; shift 1;; --ignore-cmd-help) generate_cmd_help=0; shift 1 ;; --ignore-xcodeproj) generate_xcodeproj=0; shift 1 ;; --ignore-shell-parser) generate_shell_parser=0; shift 1 ;; *) echo "Unknown option $1"; exit 1 ;; esac done if test $generate_shell_parser = 1; then ./generate-shell-parser.sh fi if test $generate_cmd_help = 1; then # It takes 300ms for the script to complete ./script/generate-cmd-help.sh fi cat > Sources/Common/versionGenerated.swift < ./Sources/Cli/subcommandDescriptionsGenerated.swift // FILE IS GENERATED FROM docs/aerospace-*.adoc files // TO REGENERATE THE FILE RUN generate.sh let subcommandDescriptions = [ $(entries) ] EOF cat < ./Sources/Common/gitHashGenerated.swift // FILE IS GENERATED BY generate.sh public let gitHash = "$(if test $generate_git_hash = 0; then echo "SNAPSHOT"; else git rev-parse HEAD; fi)" public let gitShortHash = "$(if test $generate_git_hash = 0; then echo "SNAPSHOT"; else git rev-parse --short HEAD; fi)" EOF if test $generate_xcodeproj = 1; then export XCODEGEN_AEROSPACE_VERSION=$build_version ./script/install-dep.sh --xcodegen ./.deps/xcodegen/xcodegen # https://github.com/yonaskolb/XcodeGen fi ================================================ FILE: grammar/ShellLexer.g4 ================================================ // shell lexer grammar. Powered by https://github.com/antlr/antlr4 // Use ./generate-shell-parser.sh to regenerate grammar code lexer grammar ShellLexer; TRIPLE_QUOTE : '"""' | '\'\'\'' ; // Reserved SINGLE_QUOTED_STRING : '\'' .*? '\'' ; LDQUOTE : '"' -> pushMode(IN_DSTRING) ; LPAR : '(' -> pushMode(DEFAULT_MODE) ; INTERPOLATION_START : '$(' -> pushMode(DEFAULT_MODE) ; RPAR : ')' { _ = try? popMode() } ; // Keywords (some of them are unused, just reserved) ELIF : 'elif' NL* ; IF : 'if' NL* ; SWITCH : 'switch' NL* ; CASE : 'case' NL* ; DO : 'do' NL* ; THEN : 'then' NL* ; ELSE : 'else' NL* ; FOR : 'for' NL* ; WHILE : 'while' NL* ; CATCH : 'catch' NL* ; IN : 'in' NL* ; END : 'end' NL* ; DEFER : 'defer' NL* ; AND : '&&' ; PIPE : '|' ; OR : '||' ; SEMICOLON : ';' ; NL : SPACES? ('\r')? '\n' ; WORD : ([a-zA-Z] | '.' | '_' | '-' | '/')+ ; ARG : ([a-zA-Z0-9] | '.' | '_' | '-' | '/' | '+' | ',' | '=' | '!' | '%' | '{' | '}' | '^')+ ; ESCAPE_NEWLINE : '\\' SPACES? COMMENT? '\n' -> skip ; COMMENT : '#' ~('\n')* -> skip ; SPACES : [ \t]+ -> skip ; ANY : . ; // Catch all other mode IN_DSTRING; TEXT : ~('\\' | '"' | '$')+ ; INTERPOLATION_START_IN_DSTRING : '$(' -> pushMode(DEFAULT_MODE) ; ESCAPE_SEQUENCE : '\\' . ; RDQUOTE : '"' { _ = try? popMode() } ; ================================================ FILE: grammar/ShellParser.g4 ================================================ // shell parser grammar. Powered by https://github.com/antlr/antlr4 // Use ./generate-shell-parser.sh to regenerate grammar code parser grammar ShellParser; options { tokenVocab = './grammar/ShellLexer'; } root : NL* (cmds EOF | EOF) ; // Consume ALL the input cmds : IF cmd THEN cmds? (ELIF cmd THEN cmds?)* (ELSE cmds?)? END #IfElse | cmd (SEMICOLON | NL)+ (cmds)*? #Seq | cmd #Seq ; cmd : WORD arg* #Args | cmd NL* PIPE cmd #Pipe | cmd NL* AND cmd #And | cmd NL* OR cmd #Or | LPAR cmds RPAR #Parens | LPAR cmds RPAR RPAR {notifyErrorListeners("Unbalanced parenthesis")} #CmdError | LPAR cmds {notifyErrorListeners("Unbalanced parenthesis")} #CmdError ; arg : ARG #Word | WORD #Word | SINGLE_QUOTED_STRING #SQuotedString | LDQUOTE dStringFragment* RDQUOTE #DQuotedString | INTERPOLATION_START cmds RPAR #Substitution | LDQUOTE dStringFragment* RDQUOTE RDQUOTE {notifyErrorListeners("Unbalanced quotes")} #ArgError | LDQUOTE dStringFragment* {notifyErrorListeners("Unbalanced quotes")} #ArgError | INTERPOLATION_START cmds RPAR RPAR {notifyErrorListeners("Unbalanced parenthesis")} #ArgError | INTERPOLATION_START cmds {notifyErrorListeners("Unbalanced parenthesis")} #ArgError ; dStringFragment : TEXT #Text | ESCAPE_SEQUENCE #EscapeSequence | INTERPOLATION_START_IN_DSTRING cmds RPAR #Interpolation | INTERPOLATION_START_IN_DSTRING cmds RPAR RPAR {notifyErrorListeners("Unbalanced parenthesis")} #DStringFragmentError | INTERPOLATION_START_IN_DSTRING cmds {notifyErrorListeners("Unbalanced parenthesis")} #DStringFragmentError ; ================================================ FILE: grammar/commands-bnf-grammar.txt ================================================ # EBNF-like grammar of aerospace CLI args to generate shell completion. Managed by https://github.com/adaszko/complgen # Run ./build-shell-completion.sh to generate shell completion aerospace ; aerospace -v; aerospace --version; aerospace --help; aerospace -h; ::= balance-sizes [--workspace ] | close [--quit-if-last-window|--window-id ]... | close-all-windows-but-current [--quit-if-last-window] | enable toggle | enable [--fail-if-noop] on [--fail-if-noop] | enable [--fail-if-noop] off [--fail-if-noop] | flatten-workspace-tree [--workspace ] | focus []... (left|down|up|right) []... | focus --window-id | focus --dfs-index | focus []... (dfs-next|dfs-prev) []... | focus-back-and-forth | focus-monitor [--wrap-around] (left|down|up|right) [--wrap-around] | focus-monitor [--wrap-around] (next|prev) [--wrap-around] | focus-monitor ... | fullscreen [--no-outer-gaps|--window-id ]... | fullscreen [--no-outer-gaps|--fail-if-noop|--window-id ]... on [--no-outer-gaps|--fail-if-noop|--window-id ]... | fullscreen [--fail-if-noop|--window-id ] off [--fail-if-noop|--window-id ] | join-with [--window-id ] (left|down|up|right) | layout [--window-id ] (h_tiles|v_tiles|h_accordion|v_accordion|tiles|accordion|horizontal|vertical|tiling|floating)... | macos-native-fullscreen [--fail-if-noop|--window-id ]... [on|off] [--fail-if-noop|--window-id ]... | macos-native-minimize [--window-id ] | mode | move []... (left|down|up|right) []... | move-mouse [--fail-if-noop] (monitor-lazy-center|monitor-force-center|window-lazy-center|window-force-center) [--fail-if-noop] | move-node-to-monitor []... (left|down|up|right|next|prev) []... | move-node-to-monitor []... ... []... | move-node-to-workspace [--wrap-around|--focus-follows-window|--stdin|--no-stdin]... (next|prev) [--wrap-around|--focus-follows-window|--stdin|--no-stdin]... | move-node-to-workspace [--fail-if-noop|--window-id |--focus-follows-window]... [--fail-if-noop|--window-id |--focus-follows-window]... | move-workspace-to-monitor [--wrap-around] (left|down|up|right|next|prev) [--wrap-around] | move-workspace-to-monitor [--wrap-around] ... [--wrap-around] | reload-config [--no-gui | --dry-run]... | resize [--window-id ] (smart|smart-opposite|width|height) [+|-] [--window-id ] | split [--window-id ] (horizontal|vertical|opposite) [--window-id ] | swap [--swap-focus|--wrap-around|--window-id ]... (left|down|up|right|dfs-next|dfs-prev) [--swap-focus|--wrap-around|--window-id ]... | summon-workspace [--fail-if-noop] [--fail-if-noop] | trigger-binding --mode | trigger-binding --mode | volume [--no-gui] (up|down) [--no-gui] | volume [--no-gui] (mute-toggle|mute-on|mute-off) [--no-gui] | volume [--no-gui] set [--no-gui] | workspace [--auto-back-and-forth|--fail-if-noop]... [--auto-back-and-forth|--fail-if-noop]... | workspace [--wrap-around|--stdin|--no-stdin] (next|prev) [--wrap-around|--stdin|--no-stdin] | workspace-back-and-forth | config [--json | --keys]... --get {{{ aerospace config --major-keys }}} [--json | --keys]... | config --major-keys | config --all-keys | config --config-path | debug-windows [--window-id ] | list-apps [--macos-native-hidden [no] | --format | --count | --json]... | list-exec-env-vars | list-modes [--current|--json|--count]... | list-monitors [--focused [no] | --mouse [no] | --format | --count | --json]... | list-windows [ | --format | --count | --json]... | list-windows [--format |--count|--json] (--all|--focused) [--format |--count|--json] | list-workspaces []... --monitor ... []... | list-workspaces [--format |--count|--json] (--all|--focused) [--format |--count|--json] | subscribe [--all|--no-send-initial] ... [--all|--no-send-initial] ; ::= {{{ aerospace list-windows --all --format '%{window-id}%{tab}%{app-name} - %{window-title}' }}}; ::= {{{ aerospace config --get mode --keys | xargs -I{} aerospace config --get mode.{}.binding --keys }}}; ::= {{{ aerospace config --get mode --keys }}}; ::= {{{ aerospace list-workspaces --monitor all --empty no }}}; ::= {{{ true }}}; ::= {{{ true }}}; ::= --visible [no] | --empty [no] | --format | --format | --count | --json; ::= --window-id | --boundaries | --boundaries-action ; ::= stop|fail|create-implicit-container; ::= --window-id |--focus-follows-window|--fail-if-noop|--wrap-around; ::= --window-id |--focus-follows-window|--fail-if-noop; ::= --boundaries |--boundaries-action |--ignore-floating|--wrap-around; ::= --boundaries-action |--ignore-floating|--wrap-around; ::= stop|fail|wrap-around-the-workspace|wrap-around-all-monitors; ::= workspace|all-monitors-outer-frame; ::= --workspace (visible | focused | )... | --monitor ... | --pid | --app-bundle-id ; ::= {{{ true }}}; ::= {{{ aerospace list-apps --format '%{app-bundle-id}%{tab}%{app-name}' }}}; ::= {{{ aerospace list-apps --format '%{app-pid}%{tab}%{app-name}' }}}; ::= all | mouse | focused | {{{ aerospace list-monitors --format '%{monitor-id}%{tab}%{monitor-name}' }}}; ::= focus-changed | focused-monitor-changed | focused-workspace-changed | mode-changed | window-detected | binding-triggered; ================================================ FILE: install-from-sources.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh rebuild=1 while test $# -gt 0; do case $1 in --dont-rebuild) rebuild=0; shift ;; *) echo "Unknown option $1"; exit 1 ;; esac done if test $rebuild == 1; then ./build-release.sh fi PATH="$PATH:$(brew --prefix)/bin" export PATH brew list aerospace-dev-user/aerospace-dev-tap/aerospace-dev > /dev/null 2>&1 && brew uninstall aerospace-dev-user/aerospace-dev-tap/aerospace-dev # Compatibility. Drop after a while brew list nikitabobko/local-tap/aerospace-dev > /dev/null 2>&1 && brew uninstall nikitabobko/local-tap/aerospace-dev brew list aerospace > /dev/null 2>&1 && brew uninstall aerospace which brew-install-path > /dev/null 2>&1 || brew install nikitabobko/tap/brew-install-path # Override HOMEBREW_CACHE. Otherwise, homebrew refuses to "redownload" the snapshot file # Maybe there is a better way, I don't know rm -rf /tmp/aerospace-from-sources-brew-cache HOMEBREW_CACHE=/tmp/aerospace-from-sources-brew-cache brew install-path ./.release/aerospace-dev.rb rm -rf "$(brew --prefix)/Library/Taps/aerospace-dev-user" # Compatibility. Drop after a while ================================================ FILE: legal/README.md ================================================ # LICENSE The AeroSpace itself is licensed under MIT. See [LICENSE](./LICENSE.txt) for the full license text. ## Bundled dependencies and materials AeroSpace bundles the following dependencies and uses the following materials: **HotKey**. [HotKey GitHub link](https://github.com/soffes/HotKey). [HotKey MIT license](./third-party-license/LICENSE-HotKey.txt). HotKey is used as a more convenient wrapper around macOS Carbon API to listen for global shortcuts. **TOMLKIT**. [TOMLKIT GitHub link](https://github.com/LebJe/TOMLKit). [TOMLKIT MIT license](./third-party-license/LICENSE-TOMLKIT.txt). TOMLKIT is used as a more convenient Swift wrapper around tomlplusplus C++ API. **tomlplusplus**. [tomlplusplus GitHub link](https://github.com/marzer/tomlplusplus). [tomlplusplus MIT license](./third-party-license/LICENSE-tomlplusplus.txt). tomlplusplus is used as TOML parser. tomlplusplus is used indirectly through TOMLKIT Swift API. **ANTLR v4**. [ANTLR v4 GitHub link](https://github.com/antlr/antlr4). [ANTLR BSD-3 license](./third-party-license/LICENSE-antlr.txt). ANTLR is used to parse AeroSpace built-in shell like language. **swift-collections**. [swift-collections GitHub link](https://github.com/apple/swift-collections). [swift-collections Apache 2.0 license](./third-party-license/LICENSE-swift-collections.txt). swift-collections is used for more advanced Swift collections. **ISSoundAdditions** [ISSoundAdditions GitHub link](https://github.com/InerziaSoft/ISSoundAdditions). [ISSoundAdditions MIT license](./third-party-license/LICENSE-ISSoundAdditions.txt). ISSoundAdditions is used as a convenient API to change system volume. ================================================ FILE: legal/third-party-license/LICENSE-HotKey.txt ================================================ Copyright (c) 2017–2019 Sam Soffes, http://soff.es 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. ================================================ FILE: legal/third-party-license/LICENSE-ISSoundAdditions.txt ================================================ MIT License Copyright (c) 2021 InerziaSoft - Massimo and Alessio Moiso. 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. ================================================ FILE: legal/third-party-license/LICENSE-TOMLKIT.txt ================================================ MIT License Copyright (c) 2024 Jeff Lebrun 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. ================================================ FILE: legal/third-party-license/LICENSE-antlr.txt ================================================ Copyright (c) 2012-2022 The ANTLR Project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither name of copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: legal/third-party-license/LICENSE-swift-collections.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ## Runtime Library Exception to the Apache 2.0 License: ## As an exception, if you use this Software to compile your source code and portions of this Software are embedded into the binary product as a result, you may redistribute such product without providing attribution as would otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. ================================================ FILE: legal/third-party-license/LICENSE-tomlplusplus.txt ================================================ MIT License Copyright (c) Mark Gillard 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. ================================================ FILE: lint.sh ================================================ #!/usr/bin/env bash cd "$(dirname "$0")" source ./script/setup.sh ./format.sh "$@" if sw_vers -productVersion | grep -q "^14"; then # macOS 14 # dyld[6263]: Library not loaded: /usr/lib/swift/libswiftSynchronization.dylib # Referenced from: <0A48220F-3DCA-3C6C-A54C-FE6C3B854E23> /Users/runner/work/AeroSpace/AeroSpace/.deps/periphery/dist/periphery-3.6.0.artifactbundle/periphery-3.6.0-macos/bin/periphery (built for macOS 15.0 which is newer than running OS) # Reason: tried: '/usr/lib/swift/libswiftSynchronization.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/lib/swift/libswiftSynchronization.dylib' (no such file), '/usr/lib/swift/libswiftSynchronization.dylib' (no such file, not in dyld cache) echo 'warning: periphery is disabled on macos 14' else ./script/install-dep.sh --periphery # Disable superfluous comments detection because it's buggy. todo: report to periphery maintainer ./.deps/periphery/periphery scan --quiet \ --strict \ --disable-redundant-public-analysis \ --no-superfluous-ignore-comments \ --exclude-targets \ ShellParserGenerated fi ================================================ FILE: makefile ================================================ # makefile is used to make :make command in vim work out of the box .PHONY: build test format swift-test build: ./build-debug.sh test: ./run-tests.sh swift-test: ./run-swift-test.sh format: ./format.sh lint: ./lint.sh ================================================ FILE: project.yml ================================================ # Xcode project configuration. Managed by https://github.com/yonaskolb/XcodeGen # Xcode is only used to build the release App Bundle. Debug builds only use Swift Package Manager name: AeroSpace packages: AeroSpacePackage: path: . configs: Debug: debug Release: release targets: AeroSpace: type: application platform: macOS deploymentTarget: "13.0" sources: - "Sources/AeroSpaceApp" - "resources" - "docs/config-examples/default-config.toml" dependencies: - package: AeroSpacePackage product: AppBundle # https://developer.apple.com/documentation/xcode/build-settings-reference settings: base: SWIFT_VERSION: 6.2 # Swift Language Version GENERATE_INFOPLIST_FILE: YES MARKETING_VERSION: ${XCODEGEN_AEROSPACE_VERSION} # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/20001431-108256 # Specifies whether the app runs as an agent app. If this key is set to YES, Launch Services runs the app as an agent app. # Agent apps do not appear in the Dock or in the Force Quit window (cmd-opt-esc) INFOPLIST_KEY_LSUIElement: YES CODE_SIGN_IDENTITY: ${XCODEGEN_AEROSPACE_CODE_SIGN_IDENTITY} configs: Debug: PRODUCT_NAME: AeroSpace-Debug PRODUCT_BUNDLE_IDENTIFIER: bobko.aerospace.debug Release: PRODUCT_NAME: AeroSpace PRODUCT_BUNDLE_IDENTIFIER: bobko.aerospace SWIFT_TREAT_WARNINGS_AS_ERRORS: YES GCC_TREAT_WARNINGS_AS_ERRORS: YES entitlements: path: resources/AeroSpace.entitlements properties: # Accessibility API doesn't work in sandboxed apps com.apple.security.app-sandbox: false ================================================ FILE: resources/AeroSpace.entitlements ================================================ com.apple.security.app-sandbox ================================================ FILE: resources/Assets.xcassets/AccentColor.colorset/Contents.json ================================================ { "colors" : [ { "idiom" : "universal" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: resources/Assets.xcassets/AppIcon.appiconset/Contents.json ================================================ { "images" : [ { "idiom" : "mac", "scale" : "1x", "size" : "16x16" }, { "idiom" : "mac", "scale" : "2x", "size" : "16x16" }, { "idiom" : "mac", "scale" : "1x", "size" : "32x32" }, { "idiom" : "mac", "scale" : "2x", "size" : "32x32" }, { "idiom" : "mac", "scale" : "1x", "size" : "128x128" }, { "idiom" : "mac", "scale" : "2x", "size" : "128x128" }, { "idiom" : "mac", "scale" : "1x", "size" : "256x256" }, { "idiom" : "mac", "scale" : "2x", "size" : "256x256" }, { "idiom" : "mac", "scale" : "1x", "size" : "512x512" }, { "filename" : "icon.png", "idiom" : "mac", "scale" : "2x", "size" : "512x512" } ], "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: resources/Assets.xcassets/Contents.json ================================================ { "info" : { "author" : "xcode", "version" : 1 } } ================================================ FILE: run-cli.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./build-debug.sh > /dev/null || ./build-debug.sh ./.debug/aerospace "$@" ================================================ FILE: run-debug.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./build-debug.sh ./.debug/AeroSpaceApp "$@" ================================================ FILE: run-swift-test.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh if swift test \ | sed -E '/^Test (Suite|Case).*(started|passed)/d' \ | sed -E '/^[[:space:]]+Executed.*with 0 failures/d' \ | sed -E '/ [[:digit:]]+(:[[:digit:]]+)+/s/:/;/g' # Replace colons with semicolons in dates to avoid treating these lines as files in vim then echo "✅ Swift tests have passed successfully" else echo "❌ Swift tests have failed" exit 1 fi ================================================ FILE: run-tests.sh ================================================ #!/bin/bash cd "$(dirname "$0")" source ./script/setup.sh ./script/check-uncommitted-files.sh ./build-debug.sh -Xswiftc -warnings-as-errors ./run-swift-test.sh ./.debug/aerospace -h > /dev/null ./.debug/aerospace --help > /dev/null ./.debug/aerospace -v | grep -q "0.0.0-SNAPSHOT SNAPSHOT" ./.debug/aerospace --version | grep -q "0.0.0-SNAPSHOT SNAPSHOT" ./lint.sh --check-uncommitted-files ./generate.sh ./script/check-uncommitted-files.sh echo echo "✅ All tests have passed successfully" ================================================ FILE: script/build-brew-cask.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh zip_uri='' # mandatory cask_name='' # mandatory build_version="0.0.0-SNAPSHOT" while test $# -gt 0; do case $1 in --build-version) build_version="$2"; shift 2;; --zip-uri) zip_uri="$2"; shift 2;; --cask-name) cask_name="$2"; shift 2;; *) echo "Unknown arg $1"; exit 1;; esac done if test -z "$zip_uri"; then echo "--zip-uri is mandatory" > /dev/stderr; exit 1; fi if test -z "$cask_name"; then echo "--cask-name is mandatory" > /dev/stderr; exit 1; fi case "$cask_name" in aerospace) conflicts_with_casks="conflicts_with cask: 'aerospace-dev'";; aerospace-dev) conflicts_with_casks="conflicts_with cask: 'aerospace'";; *) echo "Unknown cask name: $cask_name. Allowed cask names: aerospace, aerospace-dev" > /dev/stderr; exit 1;; esac zip_file='' if test -f "$zip_uri"; then zip_file=$zip_uri zip_uri="file://$(realpath "$zip_file")" elif grep -q '^http' <<< "$zip_uri"; then zip_file=/tmp/AeroSpace-tmp.zip rm -rf $zip_file curl -L "$zip_uri" -o $zip_file else echo "$zip_uri doesn't exist" > /dev/stderr; exit 1 fi sha=$(shasum -a 256 "$zip_file" | awk '{print $1}') cask_version=':latest' # Prevent 'Not upgrading aerospace, the latest version is already installed' zip_root_dir="AeroSpace-v$build_version" if ! grep -q SNAPSHOT <<< "$build_version"; then cask_version="'$build_version'" zip_root_dir=$(sed "s/$build_version/#{version}/g" <<< "$zip_root_dir") zip_uri=$(sed "s/$build_version/#{version}/g" <<< "$zip_uri") fi rm -f ".release/$cask_name.rb" || true mkdir -p .release cat > ".release/$cask_name.rb" <= :ventura" # macOS 13 postflight do system "xattr -d com.apple.quarantine #{staged_path}/$zip_root_dir/bin/aerospace" system "xattr -d com.apple.quarantine #{appdir}/AeroSpace.app" end app "$zip_root_dir/AeroSpace.app" binary "$zip_root_dir/bin/aerospace" binary "$zip_root_dir/shell-completion/zsh/_aerospace", target: "#{HOMEBREW_PREFIX}/share/zsh/site-functions/_aerospace" binary "$zip_root_dir/shell-completion/bash/aerospace", target: "#{HOMEBREW_PREFIX}/etc/bash_completion.d/aerospace" binary "$zip_root_dir/shell-completion/fish/aerospace.fish", target: "#{HOMEBREW_PREFIX}/share/fish/vendor_completions.d/aerospace.fish" Dir["#{staged_path}/$zip_root_dir/manpage/*"].each { |man| manpage man } end EOF ================================================ FILE: script/check-uncommitted-files.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh if ! test -z "$(git status --porcelain)"; then echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! echo !!! Uncommitted files detected !!! echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! git diff | sed 's/^/ /' echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! echo !!! Uncommitted files detected !!! echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! exit 1 fi ================================================ FILE: script/clean-project.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh ./script/clean-xcode.sh git clean -ffxd ================================================ FILE: script/clean-xcode.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh ./script/check-uncommitted-files.sh rm -rf ~/Library/Developer/Xcode/DerivedData/AeroSpace-* rm -rf ./.xcode-build rm -rf AeroSpace.xcodeproj ./generate.sh ================================================ FILE: script/generate-cmd-help.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh out_file='./Sources/Common/cmdHelpGenerated.swift' aerospace_prefix="aerospace" ____usage_prefix=" USAGE:" _______or_prefix=" OR:" ____strip_prefix=" " triple_quote='"""' cat << EOF > $out_file // FILE IS GENERATED FROM docs/aerospace-*.adoc files // TO REGENERATE THE FILE RUN generate.sh EOF for file in docs/aerospace-*.adoc; do subcommand=$(basename "$file" | sed 's/^aerospace-//' | sed 's/\.adoc$//' | sed 's/-/_/g') sed -n -E '/tag::synopsis/, /end::synopsis/ p' "$file" | \ sed '1d' | \ sed '$d' | \ sed '/^$/ d' | \ sed "1 s/^$aerospace_prefix/$____usage_prefix/" | \ sed "2,$ s/^$aerospace_prefix/$_______or_prefix/" | \ sed "s/^$____strip_prefix//" | \ sed "1 s/^/let ${subcommand}_help_generated = $triple_quote\n/" | \ sed "\$ s/$/\n${triple_quote}/" | \ sed '2,$ s/^/ /' >> $out_file done ================================================ FILE: script/install-dep.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh all=0 antlr=0 complgen=0 swiftlint=0 swiftformat=0 xcodegen=0 bundler=0 periphery=0 while test $# -gt 0; do case $1 in --antlr) antlr=1; shift ;; --complgen) complgen=1; shift ;; --swiftlint) swiftlint=1; shift ;; --xcodegen) xcodegen=1; shift ;; --swiftformat) swiftformat=1; shift ;; --bundler) bundler=1; shift ;; --periphery) periphery=1; shift ;; --all) all=1; shift ;; *) echo "Unknown option $1"; exit 1 ;; esac done get-marker() { echo ".deps/markers/$1/$(echo "$@" | shasum | awk '{print $1}').marker"; } create-marker() { dir="$(dirname "$1")" rm -rf "$dir" && mkdir -p "$dir" touch "$1" } if test $all == 1 || test $bundler == 1; then marker=$(get-marker bundler "$(cat ./Gemfile)" "$(cat ./.bundle/*)") if ! test -f "$marker"; then bundler install create-marker "$marker" fi fi if test $all == 1 || test $antlr == 1; then # https://github.com/antlr/antlr4/releases antlr_tools='antlr4-tools==0.2.1' marker=$(get-marker antlr $antlr_tools $antlr_version) if ! test -f "$marker"; then python3 -m venv .deps/python-venv source .deps/python-venv/bin/activate python3 -m pip install "$antlr_tools" create-marker "$marker" fi fi if test $all == 1 || test $complgen == 1; then # https://github.com/adaszko/complgen/releases complgen_rev=cacb3970eb marker=$(get-marker complgen $complgen_rev) if ! test -f "$marker"; then cargo install --git https://github.com/adaszko/complgen --rev $complgen_rev --root ./.deps/cargo-root create-marker "$marker" fi fi lazy-download-zip-and-link-bin() { artifact_name=$1 link=$2 sha=$3 path_inside_zip=$4 root_path=".deps/$artifact_name" marker_path=$(get-marker "$artifact_name" "$@") if ! test -f "$marker_path"; then root_dist_path="$root_path/dist" zip_name="zip.zip" zip_path="$root_dist_path/$zip_name" rm -rf "$root_path" && mkdir -p "$root_dist_path" curl -L "$link" -o "$zip_path" diff --color <(echo "$sha") <(shasum -a 256 "$zip_path") (cd "$root_dist_path" && unzip "$zip_name") (cd "$root_path" && ln -s "./dist/$path_inside_zip" "$artifact_name") create-marker "$marker_path" fi } if test $all == 1 || test $swiftlint == 1; then # https://github.com/realm/SwiftLint/releases swiftlint_version=0.62.2 lazy-download-zip-and-link-bin \ swiftlint \ https://github.com/realm/SwiftLint/releases/download/$swiftlint_version/SwiftLintBinary.artifactbundle.zip \ '3047357eee0838a0bafc7a6e65cd1aad61734b30d7233e28f3434149fe02f522 .deps/swiftlint/dist/zip.zip' \ SwiftLintBinary.artifactbundle/macos/swiftlint fi if test $all == 1 || test $xcodegen == 1; then # https://github.com/yonaskolb/XcodeGen/releases xcodegen_version=2.44.1 lazy-download-zip-and-link-bin \ xcodegen \ https://github.com/yonaskolb/XcodeGen/releases/download/$xcodegen_version/xcodegen.artifactbundle.zip \ 'cfa4e1ee82fc4c95bf7bd8f7db1fda6bd073605c76a8d5cbce50c54a81867eb2 .deps/xcodegen/dist/zip.zip' \ xcodegen.artifactbundle/xcodegen-$xcodegen_version-macosx/bin/xcodegen fi if test $all == 1 || test $swiftformat == 1; then # https://github.com/nicklockwood/SwiftFormat/releases swiftformat_version=0.58.6 lazy-download-zip-and-link-bin \ swiftformat \ https://github.com/nicklockwood/SwiftFormat/releases/download/$swiftformat_version/swiftformat.artifactbundle.zip \ 'd2ee571b3f15c173b1789b82b9fcf1e799cff66de0ae9f6839bd35aa8e9b9608 .deps/swiftformat/dist/zip.zip' \ swiftformat.artifactbundle/swiftformat-$swiftformat_version-macos/bin/swiftformat fi if test $all == 1 || test $periphery == 1; then # https://github.com/peripheryapp/periphery/releases periphery_version=3.6.0 lazy-download-zip-and-link-bin \ periphery \ https://github.com/peripheryapp/periphery/releases/download/$periphery_version/periphery-$periphery_version.zip \ '983cb6bad09b7030f0ec151e05f650dbf450eb624bd361a0ad89c59fdbf18182 .deps/periphery/dist/zip.zip' \ periphery fi ================================================ FILE: script/publish-release.sh ================================================ #!/bin/bash cd "$(dirname "$0")/.." source ./script/setup.sh build_version="" cask_git_repo_path="" site_git_repo_path="" while test $# -gt 0; do case $1 in --build-version) build_version="$2"; shift 2;; --cask-git-repo-path) cask_git_repo_path="$2"; shift 2;; --site-git-repo-path) site_git_repo_path="$2"; shift 2;; *) echo "Unknown option $1"; exit 1;; esac done if test -z "$build_version"; then echo "--build-version flag is mandatory" > /dev/stderr exit 1 fi if ! test -d "$cask_git_repo_path"; then echo "--cask-git-repo-path is a mandatory flag that must point to existing directory" > /dev/stderr exit 1 fi if ! test -d "$site_git_repo_path"; then echo "--site-git-repo-path is a mandatory flag that must point to existing directory" > /dev/stderr exit 1 fi ./run-tests.sh ./build-release.sh --build-version "$build_version" git tag -a "v$build_version" -m "v$build_version" && git push git@github.com:nikitabobko/AeroSpace.git "v$build_version" link="https://github.com/nikitabobko/AeroSpace/releases/new?tag=v$build_version" open "$link" || { echo "$link"; exit 1; } sleep 1 open -R "./.release/AeroSpace-v$build_version.zip" echo "Please upload .zip to GitHub release and hit Enter" read -r ./script/build-brew-cask.sh \ --cask-name aerospace \ --zip-uri "https://github.com/nikitabobko/AeroSpace/releases/download/v$build_version/AeroSpace-v$build_version.zip" \ --build-version "$build_version" eval "$cask_git_repo_path/pin.sh" cp -r .release/aerospace.rb "$cask_git_repo_path/Casks/aerospace.rb" rm -rf "${site_git_repo_path:?}/*" # https://www.shellcheck.net/wiki/SC2115 cp -r .site/* "$site_git_repo_path" ================================================ FILE: script/reset-accessibility-permission-for-debug.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) tccutil reset Accessibility bobko.AeroSpace.debug ================================================ FILE: script/setup.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) # Don't forget to also update ./ShellParserGenerated/Package.swift export antlr_version="4.13.1" add-optional-dep-to-bin() { if /usr/bin/which "$1" &> /dev/null; then /bin/cat > ".deps/bin/${2:-$1}" < /dev/null; then swiftly run swift "$@" else echo "warning: swiftly is not installed. Fallback to plain swift. Swift compilation might not be reproducible" > /dev/stderr /usr/bin/env swift --version /usr/bin/env swift "$@" fi } xcodebuild-pretty() { log_file="$1" shift # Mute stderr # 2024-02-12 23:48:11.713 xcodebuild[60777:7403664] [MT] DVTAssertions: Warning in /System/Volumes/Data/SWE/Apps/DT/BuildRoots/BuildRoot11/ActiveBuildRoot/Library/Caches/com.apple.xbs/Sources/IDEFrameworks/IDEFrameworks-22269/IDEFoundation/Provisioning/Capabilities Infrastructure/IDECapabilityQuerySelection.swift:103 # Details: createItemModels creation requirements should not create capability item model for a capability item model that already exists. # Function: createItemModels(for:itemModelSource:) # Thread: <_NSMainThread: 0x6000037202c0>{number = 1, name = main} # Please file a bug at https://feedbackassistant.apple.com with this warning message and any useful information you can provide. if /usr/bin/which xcbeautify &> /dev/null; then /usr/bin/xcrun xcodebuild "$@" 2>&1 | tee "$log_file" | xcbeautify --quiet # Only print tasks that have warnings or errors echo "The full unmodified xcodebuild log is saved to $log_file" else /usr/bin/xcrun xcodebuild "$@" 2>&1 | tee "$log_file" fi }