Closed remi-gelinas closed 1 month ago
I had an idea to provide flag for custom config file location, when I was implementing --config-path
option in config
command. But I stumbled upon the following design problem:
aerospace reload-config --path /custom/path/to/aerospace.toml
# Shall the second invocation of reload-config read the config from default location
# or keep reading it from the custom location?
aerospace reload-config
Surely, two flags can be introduced: --persistent-config-path
and --one-time-config-path
, but I'm afraid of unnecessary complications, and would love to get on only with a single flag if possible. What would be your preferred option and why?
I also noticed that you interact with AeroSpace.app server directly. I've not considered the server CLI as an API entry point. --started-at-login
is an internal implementation detail. At least for now. (I assume that you meant --started-at-login
instead of --running-at-start
. --running-at-start
is not an existing flag)
I'm curious to learn more about your use case. I don't know a lot about nix and nix-darwin. All I know is how nix stores the packages to achieve better dependency control. Is AeroSpace installed via Nix, so that's why you have to interact with AeroSpace.app directly?
As to the design problem, personally I think I'd like to see the server be started with the target config path, and then consider that in all following operations. i.e. if I run it with -c /etc/aerospace.toml
, reload-config
would reload at that location until the server is restarted with a different config location.
As for why, purely because I think it would follow most daemon design that start parameters don't change unless the daemon is restarted with new ones, but that's just my view :)
As for the nix
use case, it'll maybe take a bit more explanation for why this would solve a problem I have. nix-darwin
provides modules for configuring a MacOS system (users, system defaults, available packages in the environment, etc.), but the specifics are largely unimportant. Services bundled into nix-darwin
modules that need launchd agents typically are installed along with a deterministic agent config based on the Nix store path of the app bundle or binary, and are pointed to a deterministic config again created in the Nix store.
Without getting into the weeds about Nix syntax and the module system, the intent is for a particular revision of a system config to create the same launch agents that reference the same target binary all the time - and nix-darwin
manages those agents and their creation and lifecycle.
For a good example, take the nix-darwin
module for yabai, here. This definition results in a config file being created in the Nix store, the yabai binary being available in the system PATH
, and a launchd agent being created that calls the exact version of the target yabai binary in the store with the config generated in the store being passed in.
If you were to update the yabai package to a new version, for example, nix-darwin
will ensure that the resultant launchd agent is unloaded, amended to point to the new correct binary, and then reloaded. Where this breaks when using AeroSpace is because it manages its own agent, if I update the app bundle available in my environment, as it stands I have to stop the AeroSpace service, delete the launch agent, and restart with the new app bundle to have the new agent created and pointing to the correct version of the bundle.
I recognize that this is an oddly narrow use case, and probably not one you intended to support when designing AeroSpace in the first place, so I'll absolutely accept telling me to go kick rocks if it's an out of scope change :) That said, adjusting for out-of-band agent management would also support other configuration management tools that manage agent lifecycle that aren't necessarily Nix - and personally, I could absolutely see a use case for being able to point the binary to a config in order to validate it (though that would likely require the config validation that's done in reload-config
to be split out into a separate command, for example I could see a lot of use of aerospace validate-config -c <config_file>
).
EDIT: And yes, I definitely did mean --started-at-login
, sorry about that!
Implemented in 0.13.0-Beta:
USAGE: ./.debug/AeroSpace-Debug.app/Contents/MacOS/AeroSpace-Debug [<options>]
OPTIONS:
-h, --help Print help
-v, --version Print AeroSpace.app version
--started-at-login Make AeroSpace.app think that it is started at login
--config-path <path> Config path. If the file exists it will takes priority
over ~/.aerospace.toml and ${XDG_CONFIG_HOME}/aerospace/aerospace.toml
Awesome, thank you for the flag! I'll get to testing this today. Does this change agent behaviour at all? i.e Can I manually create a launch agent with the config file flag, or will that still misbehave?
or will that still misbehave?
What do you mean?
or will that still misbehave?
What do you mean?
Actually, I had assumed that because it was an internal implementation detail as you mentioned earlier that it wasn't really meant for running directly. However, I wrote my nix-darwin module to create a launch agent running the server without the --started-at-login
flag and pointing the --config-path
at the generated config in the Nix store, and it works like a charm!
Hey @remi-gelinas, are you planning to add your module to nix-darwin? I had thought on writing a module myself seeing it was not available there, but checking here I saw you already have one. Would be great to see it!
Hey @remi-gelinas, are you planning to add your module to nix-darwin? I had thought on writing a module myself seeing it was not available there, but checking here I saw you already have one. Would be great to see it!
I was planning on it at some point, but I'm not sure if Aerospace itself is present in nixpkgs. I have a derivation written to pull the built release I could add to nixpkgs.
Sure, I can get the package itself and the module upstreamed
{ stdenv, fetchzip, installShellFiles }:
stdenv.mkDerivation rec {
pname = "aerospace";
version = "0.14.2-Beta";
nativeBuildInputs = [ installShellFiles ];
buildPhase = "";
installPhase = ''
mkdir -p $out/bin
cp bin/aerospace $out/bin
mkdir -p $out/Applications
cp -r AeroSpace.app $out/Applications/AeroSpace.app
installManPage manpage/*
'';
src = fetchzip {
url =
"https://github.com/nikitabobko/AeroSpace/releases/download/v${version}/AeroSpace-v${version}.zip";
hash = "sha256-v2D/IV9Va0zbGHEwSGt6jvDqQYqha290Lm6u+nZTS3A=";
};
}
Options:
{ config, lib, pkgs, osConfig, ... }:
with lib;
let
inherit (pkgs) system;
inherit (osConfig.flake.packages.${system}) aerospace;
cfg = config.programs.aerospace;
tomlFormat = pkgs.formats.toml { };
in {
options = {
programs.aerospace = {
enable = mkEnableOption "an i3-like window manager for macOS";
package = mkOption {
type = types.package;
default = aerospace;
description = "The aerospace package to install.";
};
settings = mkOption {
inherit (tomlFormat) type;
default = { };
description = "Configuration for aerospace.";
};
enableJankyBorders = mkOption {
type = types.bool;
default = true;
description = "Enable the JankyBorders package alongside aerospace.";
};
jankybordersPackage = mkOption {
type = types.package;
default = pkgs.jankyborders;
description = "The JankyBorders package to install.";
};
aerospaceAppPath = mkOption {
type = types.str;
default = "${cfg.package}/Applications/AeroSpace.app";
description = "Path to the Aerospace.app bundle.";
};
runOnStartup = mkOption {
type = types.bool;
default = true;
description = "Run AeroSpace.app on startup.";
};
};
};
config = mkIf cfg.enable {
home.packages = [ cfg.package ]
++ lib.optional cfg.enableJankyBorders cfg.jankybordersPackage;
xdg.configFile = {
"aerospace/aerospace.toml" = mkIf (cfg.settings != { }) {
source = tomlFormat.generate "config" cfg.settings;
};
};
};
}
{ pkgs, ... }: {
imports = [ ./aerospace.nix ];
programs.aerospace = {
enable = true;
settings = {
# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization
enable-normalization-flatten-containers = false;
enable-normalization-opposite-orientation-for-nested-containers = false;
after-startup-command = [
"exec-and-forget ${pkgs.jankyborders}/bin/borders style=round width=10 active_color=0xffeba0ac"
];
# Window detection. See: https://nikitabobko.github.io/AeroSpace/guide.html#on-window-detected-callback
# Common app ids: https://nikitabobko.github.io/AeroSpace/goodness#popular-apps-ids
# This is a workaround for apps that have no window decorations not being detected.
on-window-detected = [
{
${"if"}.app-id = "net.kovidgoyal.kitty";
run = "layout tiling";
}
{
${"if"}.app-id = "com.brave.Browser";
run = "layout tiling";
}
];
workspace-to-monitor-force-assignment = {
"1" = [ "C34H89x" 2 3 ];
"2" = [ "C34H89x" 2 3 ];
"3" = [ "C34H89x" 2 3 ];
"4" = [ "^built-in retina display$" ];
"5" = [ "^built-in retina display$" ];
};
gaps = {
inner = {
horizontal = 10;
vertical = 10;
};
outer = {
left = 10;
bottom = 10;
top = 10;
right = 10;
};
};
# 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
# - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual,
# keypadMinus, keypadMultiply, keypadPlus
# - Arrows. left, down, up, right
mode = {
main.binding = {
"alt-h" = [ ]; # Disable alt-h hiding windows
"alt-f" =
"flatten-workspace-tree"; # The oh-shit I fucked up, hit reset button
"alt-q" = "close";
"alt-slash" = "layout tiles horizontal vertical";
# Focus active window in direction
"alt-left" =
"focus left --boundaries all-monitors-outer-frame --boundaries-action wrap-around-all-monitors";
"alt-down" = "focus down";
"alt-up" = "focus up";
"alt-right" =
"focus right --boundaries all-monitors-outer-frame --boundaries-action wrap-around-all-monitors";
# move a window in direction
"alt-shift-left" = "move left";
"alt-shift-down" = "move down";
"alt-shift-up" = "move up";
"alt-shift-right" = "move right";
# Move window to a monitor in general direction
"alt-shift-ctrl-left" = "move-node-to-monitor left";
"alt-shift-ctrl-down" = "move-node-to-monitor down";
"alt-shift-ctrl-up" = "move-node-to-monitor up";
"alt-shift-ctrl-right" = "move-node-to-monitor right";
# Cycle through workspaces
"alt-tab" = "workspace-back-and-forth";
# Float a window
"alt-shift-f" = "layout floating tiling"; # "floating toggle" in i3
# Workspaces replace MacOS native Virtual Desktops
"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-r" = "mode resize";
};
resize.binding = {
"left" = "resize width -50";
"up" = "resize height +50";
"down" = "resize height -50";
"right" = "resize width +50";
"enter" = "mode main";
"esc" = "mode main";
};
};
};
};
}
Pick the meat off the bones all you like 😃
Only part I'm having issues with it getting the on-window-detected
TOML conversion perfect.
on-window-detected = [
{
"if.app-id" = "net.kovidgoyal.kitty";
run = "layout tiling";
}
{
"if.app-id" = "com.brave.Browser";
run = "layout tiling";
}
];
Above should work in theory, but doesn't seem to like the quotes in generated TOML.
@nephalemsec "if.app-id" isn't the structure it's expecting - try "if".app-id.
@nephalemsec "if.app-id" isn't the structure it's expecting - try "if".app-id.
Yeah have tried that too. Nothing seems to work to get brave tiling properly for me.
This is the result of using "if".app-id
:
I'd love to see a
--config
or-c
flag for the CLI that would allow one to specify the target config file. This would not only help in settings where you're testing different configs (directly running theAeroSpace
binary in the app bundle to test configs for example, though I realize the intended way to do this is probably to hot reload the config as you make changes).In my very narrow use case, this would also allow system management tools that manage available packages and launchd services to keep the AeroSpace launchd agent in sync with the available app bundle. I use Nix and
nix-darwin
to manage my hosts, andyabai
provides the-c
flag to specify the target config - this allows me to keep the launchd service in sync with the provided binary and config.For example, one could specify a config file outside of the two allowed config locations AeroSpace currently provides:
AeroSpace.app/Content/MacOS/AeroSpace --running-at-start -c /etc/aerospace.toml