This repository has no description
8.5 kB
261 lines
1{ self, ... }:
2{
3 flake.wrapperModules.workspace-manager =
4 {
5 config,
6 lib,
7 pkgs,
8 ...
9 }:
10 let
11 cfg = config.lass.workspace-manager;
12
13 workspaceType = lib.types.submodule {
14 options = {
15 directory = lib.mkOption {
16 type = lib.types.str;
17 description = "Directory to use for this workspace";
18 };
19 on_enter = lib.mkOption {
20 type = lib.types.listOf lib.types.str;
21 default = [ ];
22 description = "Commands to run when entering this workspace";
23 };
24 on_leave = lib.mkOption {
25 type = lib.types.listOf lib.types.str;
26 default = [ ];
27 description = "Commands to run when leaving this workspace";
28 };
29 on_create = lib.mkOption {
30 type = lib.types.listOf lib.types.str;
31 default = [ ];
32 description = "Commands to run in a tmux session when the workspace is first entered. Each command gets its own tmux window with visible logs. Reattach with: tmux attach -t ws-<name>";
33 };
34 };
35 };
36
37 # Generate a directory with JSON files for each declared workspace
38 systemConfigDir = pkgs.linkFarm "workspace-manager-configs" (
39 lib.mapAttrsToList (name: ws: {
40 name = "${name}.json";
41 path = pkgs.writeText "${name}.json" (
42 builtins.toJSON {
43 inherit (ws)
44 directory
45 on_enter
46 on_leave
47 on_create
48 ;
49 }
50 );
51 }) cfg.workspaces
52 );
53 in
54 {
55 options.lass.workspace-manager = {
56 enable = lib.mkEnableOption "workspace manager daemon for sway";
57
58 package = lib.mkOption {
59 type = lib.types.package;
60 default = self.packages.${pkgs.system}.workspace-manager-daemon;
61 description = "The workspace-manager-daemon package to use";
62 };
63
64 workspaces = lib.mkOption {
65 type = lib.types.attrsOf workspaceType;
66 default = { };
67 description = "Declarative workspace configurations (take precedence over user configs)";
68 example = lib.literalExpression ''
69 {
70 dev = {
71 directory = "/home/user/src/myproject";
72 on_enter = [ "notify-send 'Entered dev workspace'" ];
73 };
74 music = {
75 directory = "/home/user/Music";
76 };
77 }
78 '';
79 };
80
81 configDir = lib.mkOption {
82 type = lib.types.str;
83 default = "~/workspaces";
84 description = "Directory containing user workspace configuration files";
85 };
86
87 defaultDirectory = lib.mkOption {
88 type = lib.types.str;
89 default = "~";
90 description = "Default directory for workspaces without configuration";
91 };
92
93 terminalCommand = lib.mkOption {
94 type = lib.types.str;
95 default = "kitty";
96 description = "Terminal emulator to use for on_create commands. Must support '-T title' for setting the window title.";
97 };
98
99 onNewHandler = lib.mkOption {
100 type = lib.types.nullOr lib.types.str;
101 default = "${self.packages.${pkgs.system}.workspace-on-new}/bin/workspace-on-new";
102 description = "Script called when entering a workspace for the first time. Exit 0 to restore (run on_create), non-zero to skip.";
103 };
104 };
105
106 config = lib.mkIf cfg.enable {
107 systemd.user.services.workspace-manager = {
108 description = "Workspace Manager Daemon";
109 partOf = [ "sway-session.target" ];
110 wantedBy = [ "sway-session.target" ];
111 serviceConfig = {
112 Type = "simple";
113 ExecStart = "${cfg.package}/bin/workspace-manager-daemon";
114 Restart = "on-failure";
115 RestartSec = 5;
116 Environment = [
117 "WORKSPACE_MANAGER_CONFIG_DIR=${cfg.configDir}"
118 "WORKSPACE_MANAGER_SYSTEM_CONFIG_DIR=${systemConfigDir}"
119 "WORKSPACE_MANAGER_DEFAULT_DIR=${cfg.defaultDirectory}"
120 "WORKSPACE_MANAGER_TERMINAL=${cfg.terminalCommand}"
121 ]
122 ++ lib.optional (cfg.onNewHandler != null) "WORKSPACE_MANAGER_ON_NEW_HANDLER=${cfg.onNewHandler}";
123 };
124 };
125 };
126 };
127
128 perSystem =
129 {
130 pkgs,
131 ...
132 }:
133 let
134 python = pkgs.python3;
135 in
136 {
137 packages.workspace-manager =
138 (pkgs.writeShellApplication {
139 name = "workspace-manager";
140 runtimeInputs = [
141 pkgs.socat
142 pkgs.jq
143 ];
144 text = builtins.readFile ./workspace-manager.sh;
145 }).overrideAttrs
146 { passthru.usage = builtins.readFile ./usage.kdl; };
147
148 packages.workspace-menu = pkgs.writeShellApplication {
149 name = "workspace-menu";
150 runtimeInputs = [
151 pkgs.sway
152 pkgs.jq
153 pkgs.rofi
154 pkgs.libnotify
155 pkgs.tmux
156 pkgs.kitty
157 pkgs.findutils
158 pkgs.gnused
159 pkgs.coreutils
160 pkgs.gnugrep
161 ];
162 text = builtins.readFile ./workspace-menu.sh;
163 };
164
165 packages.workspace-on-new = pkgs.writeShellApplication {
166 name = "workspace-on-new";
167 runtimeInputs = [
168 pkgs.jq
169 pkgs.rofi
170 pkgs.coreutils
171 pkgs.gnused
172 ];
173 text = builtins.readFile ./workspace-on-new.sh;
174 };
175
176 packages.workspace-manager-daemon = pkgs.stdenv.mkDerivation {
177 pname = "workspace-manager-daemon";
178 version = "0.1.0";
179 src = ./.;
180 nativeBuildInputs = [
181 pkgs.makeWrapper
182 pkgs.ruff
183 python.pkgs.mypy
184 ];
185 doCheck = true;
186 checkPhase = ''
187 runHook preCheck
188 ruff check daemon.py
189 ruff format --check daemon.py
190 mypy --strict daemon.py
191 runHook postCheck
192 '';
193 installPhase = ''
194 runHook preInstall
195 mkdir -p $out/bin
196 cp daemon.py $out/bin/workspace-manager-daemon
197 chmod +x $out/bin/workspace-manager-daemon
198 wrapProgram $out/bin/workspace-manager-daemon \
199 --prefix PATH : ${python}/bin:${pkgs.sway}/bin:${pkgs.tmux}/bin
200 runHook postInstall
201 '';
202 meta.mainProgram = "workspace-manager-daemon";
203 };
204
205 # Open Firefox for the current workspace
206 packages.workspace-browser = pkgs.writeShellScriptBin "workspace-browser" ''
207 exec firefox "$@"
208 '';
209
210 # Native messaging host for the workspace-tabs Firefox extension
211 packages.workspace-tabs-native-host = pkgs.stdenv.mkDerivation {
212 pname = "workspace-tabs-native-host";
213 version = "0.1.0";
214 src = ./.;
215 nativeBuildInputs = [
216 pkgs.makeWrapper
217 pkgs.ruff
218 python.pkgs.mypy
219 ];
220 doCheck = true;
221 checkPhase = ''
222 runHook preCheck
223 ruff check workspace-tabs-host.py
224 ruff format --check workspace-tabs-host.py
225 mypy --strict workspace-tabs-host.py
226 runHook postCheck
227 '';
228 installPhase = ''
229 runHook preInstall
230 mkdir -p $out/bin $out/lib/mozilla/native-messaging-hosts
231 cp workspace-tabs-host.py $out/bin/workspace-tabs-host
232 chmod +x $out/bin/workspace-tabs-host
233 wrapProgram $out/bin/workspace-tabs-host \
234 --prefix PATH : ${python}/bin:${pkgs.sway}/bin
235
236 cat > $out/lib/mozilla/native-messaging-hosts/workspace_tabs.json <<EOF
237 {
238 "name": "workspace_tabs",
239 "description": "Native messaging host for Workspace Tabs extension",
240 "path": "$out/bin/workspace-tabs-host",
241 "type": "stdio",
242 "allowed_extensions": ["workspace-tabs@workspace-manager"]
243 }
244 EOF
245 runHook postInstall
246 '';
247 };
248
249 # Firefox extension for workspace tab management (packaged as .xpi)
250 packages.workspace-tabs-extension =
251 pkgs.runCommand "workspace-tabs-extension"
252 {
253 nativeBuildInputs = [ pkgs.zip ];
254 }
255 ''
256 mkdir -p $out
257 cd ${./firefox-extension}
258 zip -r "$out/workspace-tabs@workspace-manager.xpi" ./*
259 '';
260 };
261}