]> gitweb.ps.run Git - flake_thinkpad/blob - home.nix
Initial commit
[flake_thinkpad] / home.nix
1 { inputs, config, pkgs, lib, wallpaper, ... }:
2
3 let
4   theme = pkgs.qogir-theme.override { tweaks = [ "square" ]; };
5   markdownStyleHeader = pkgs.writeText "style.html" ''
6     <style type="text/css">
7     html{
8       margin:40px auto;
9       max-width:650px;
10       line-height:1.6;
11       font-size:18px;
12       color:#444;
13       background-color:#EEE;
14       padding:0 10px
15     }
16     @media (prefers-color-scheme: dark) {
17     html{
18       color:#CCC;
19       background-color:#333;
20     }
21     }
22     h1,h2,h3{line-height:1.2}
23     </style>
24   '';
25   markdownCaddyfile = pkgs.writeText "Caddyfile" ''
26     :8123
27
28     encode zstd gzip
29     templates
30     file_server browse {
31             root "/"
32             hide ".*"
33     }
34
35     @md <<CEL
36       {file}.endsWith(".md")
37       || {file}.endsWith(".MD")
38       CEL
39
40     header @md Content-Type "text/html; charset=UTF-8"
41     respond @md <<HTML
42             <!DOCTYPE html>
43             <html>
44                     <head>
45                             <title>{{ "{file.base}" }}</title>
46                             <link rel="stylesheet" href="https://sindresorhus.com/github-markdown-css/github-markdown.css">
47                             <style>
48                             .markdown-body {
49                                         box-sizing: border-box;
50                                         min-width: 200px;
51                                         max-width: 980px;
52                                         margin: 0 auto;
53                                         padding: 45px;
54                                 }
55                             </style>
56                     </head>
57                     <body class="markdown-body">
58                     {{
59                     markdown (include "{path}")
60                     }}
61                     </body>
62             </html>
63             HTML 200
64   '';
65 in
66 {
67   # Home Manager needs a bit of information about you and the paths it should
68   # manage.
69   home.username = "ps";
70   home.homeDirectory = "/home/ps";
71
72   services.xcape = {
73     enable = true;
74     mapExpression = {
75       Caps_Lock = "Escape";
76       Super_L = "Alt_L|F1";
77     };
78   };
79
80   xfconf.settings = {
81     xfce4-desktop = {
82       "backdrop/screen0/monitor0/image-path" = "${wallpaper}";
83       "backdrop/screen0/monitor0/image-show" = true;
84       "backdrop/screen0/monitor0/image-style" = 5;
85     };
86   };
87
88   systemd.user.services.markdownCaddy = {
89     Unit = {
90       Description = "Run a web server serving Markdown files.";
91       Wants = [ "network-online.target" ];
92       After = [ "network-online.target" ];
93     };
94     Install = {
95       WantedBy = [ "default.target" ];
96     };
97     Service = {
98       WorkingDirectory = "/";
99       ExecStart = "${pkgs.writeShellScript "markdown-caddy" ''
100         ${pkgs.caddy}/bin/caddy run --config ${markdownCaddyfile} --adapter caddyfile
101       ''}";
102     };
103   };
104   
105   xsession.windowManager.i3 = {
106     enable = true;
107     config = {
108       bars = [];
109       modifier = "Mod4";
110       terminal = "${pkgs.kitty}/bin/kitty";
111       gaps = {
112         inner = 5;
113       };
114       keybindings =
115         let
116           mod = config.xsession.windowManager.i3.config.modifier;
117           i3-next = pkgs.writers.writePython3 "i3-next" {} ''
118             import json
119             from subprocess import check_output
120
121             workspaces = json.loads(check_output(
122               ["${pkgs.i3}/bin/i3-msg",
123                "-t", "get_workspaces"]))
124
125             outputs = {}
126             activeOutput = ""
127             activeWorkspace = -1
128
129             for w in workspaces:
130                 output = w["output"]
131                 workspace = int(w["num"])
132                 if output not in outputs:
133                     outputs[output] = set()
134                 outputs[output].add(workspace)
135                 if w["focused"]:
136                     activeOutput = output
137                     activeWorkspace = workspace
138
139             workspacesSorted = sorted(outputs[activeOutput])
140             activeWorkspaceIndex = workspacesSorted.index(activeWorkspace)
141
142             if activeWorkspaceIndex < len(workspacesSorted) - 1:
143                 print(workspacesSorted[activeWorkspaceIndex + 1])
144             else:
145                 allWorkspaces = set()
146                 for o in outputs.values():
147                     allWorkspaces = allWorkspaces.union(o)
148                 allWorkspacesSorted = sorted(allWorkspaces)
149                 print(allWorkspacesSorted[-1]+1)
150           '';
151   
152           i3-empty = pkgs.writers.writePython3 "i3-empty" {} ''
153             import json
154             from subprocess import check_output
155
156             workspaces = json.loads(check_output(
157               ["${pkgs.i3}/bin/i3-msg",
158                "-t", "get_workspaces"]))
159
160             nextWorkspace = 1
161       
162             for w in workspaces:
163                 wNum = int(w["num"])
164                 if wNum >= nextWorkspace:
165                     nextWorkspace = wNum + 1
166
167             print(nextWorkspace)
168           '';
169           
170           i3-max = pkgs.writers.writePython3 "i3-max" {} ''
171             import json
172             from subprocess import check_output
173
174             workspaces = json.loads(check_output(
175               ["${pkgs.i3}/bin/i3-msg",
176                "-t", "get_workspaces"]))
177
178             result = "MAX"
179       
180             for w in workspaces:
181                 wName = w["name"]
182                 wFocused = w["focused"]
183                 if wName == "MAX" and wFocused:
184                     result = "back_and_forth"
185                     break
186
187             print(f"move window to workspace {result}; workspace {result}")
188           '';
189           i3-move-max = pkgs.writers.writePython3 "i3-move-max" {} ''
190             import json
191             from subprocess import check_output
192
193             workspaces = json.loads(check_output(
194               ["${pkgs.i3}/bin/i3-msg",
195                "-t", "get_workspaces"]))
196
197             result = "MAX"
198       
199             for w in workspaces:
200                 wName = w["name"]
201                 wFocused = w["focused"]
202                 if wName == "MAX" and wFocused:
203                     result = "back_and_forth"
204                     break
205
206             print(f"workspace {result}")
207           '';
208         in lib.mkOptionDefault
209           {
210             # "${mod}+d" = "exec --no-startup-id krunner";
211             "${mod}+Shift+p" = "exec --no-startup-id set-wallpaper";
212             "${mod}+Shift+Return" = "exec --no-startup-id ${pkgs.kitty}/bin/kitty -d $(${pkgs.xcwd}/bin/xcwd)";
213             "${mod}+BackSpace" = "kill";
214             "${mod}+Prior" = "workspace prev_on_output";
215             "${mod}+Next" = "exec --no-startup-id i3-msg workspace number $(${i3-next})";
216             "${mod}+End" = "exec --no-startup-id i3-msg workspace $(${i3-empty})";
217             "${mod}+Shift+Prior" = "move container to workspace prev_on_output";
218             "${mod}+Shift+Next" = "exec --no-startup-id i3-msg move container to workspace number $(${i3-next})";
219             "${mod}+Shift+End" = "exec --no-startup-id i3-msg move container to workspace $(${i3-empty})";
220             "${mod}+Ctrl+Left" = "move workspace to output left";
221             "${mod}+Ctrl+Right" = "move workspace to output right";
222             "${mod}+y" = "exec ${pkgs.scrcpy}/bin/scrcpy -d --no-audio";
223             "${mod}+n" = "exec ${pkgs.kitty}/bin/kitty ${pkgs.helix}/bin/hx -w ~/sync/txt ~/sync/txt";
224             "${mod}+m" = "exec --no-startup-id i3-msg $(${i3-max})";
225             "${mod}+Shift+m" = "exec --no-startup-id i3-msg $(${i3-move-max})";
226           };
227     };
228     extraConfig = ''
229       for_window [window_role="pop-up"] floating enable
230       for_window [window_role="task_dialog"] floating enable
231       # for_window [workspace="0"] floating enable
232
233       for_window [class="kitty-popup"] floating enable
234       for_window [class="Xfce4-appfinder"] floating enable
235       for_window [class=".blueman-manager-wrapped"] floating enable
236       for_window [class="yakuake"] floating enable
237       for_window [class="systemsettings"] floating enable
238       for_window [title="win7"] floating enable; border none
239
240       # bindsym XF86AudioRaiseVolume exec --no-startup-id qdbus org.kde.kglobalaccel /component/kmix invokeShortcut "increase_volume"
241       # bindsym XF86AudioLowerVolume exec --no-startup-id qdbus org.kde.kglobalaccel /component/kmix invokeShortcut "decrease_volume"
242       # bindsym XF86AudioMute exec --no-startup-id qdbus org.kde.kglobalaccel /component/kmix invokeShortcut "mute"
243       # bindsym XF86AudioMicMute exec --no-startup-id qdbus org.kde.kglobalaccel /component/kmix invokeShortcut "mic_mute"
244
245       # class                 border  backgr. text    indicator child_border
246       client.focused          #000000bf #000000bf #e6ebef #000000bf   #000000bf
247       client.focused_inactive #00000080 #00000080 #e6ebef #00000080   #00000080
248       client.unfocused        #00000040 #00000040 #e6ebef #00000040   #00000040
249       client.urgent           #2f343a #900000 #e6ebef #900000   #2f343a
250       client.placeholder      #000000 #0c0c0c #e6ebef #000000   #0c0c0c
251
252       focus_follows_mouse no
253       mouse_warping none
254       popup_during_fullscreen all
255     '';
256   };
257
258   home.file.".config/awesome/rc.lua".text = ''
259 -- If LuaRocks is installed, make sure that packages installed through it are
260 -- found (e.g. lgi). If LuaRocks is not installed, do nothing.
261 pcall(require, "luarocks.loader")
262
263 -- Standard awesome library
264 local gears = require("gears")
265 local awful = require("awful")
266 require("awful.autofocus")
267 -- Widget and layout library
268 local wibox = require("wibox")
269 -- Theme handling library
270 local beautiful = require("beautiful")
271 -- Notification library
272 local naughty = require("naughty")
273 local menubar = require("menubar")
274 local hotkeys_popup = require("awful.hotkeys_popup")
275 -- Enable hotkeys help widget for VIM and other apps
276 -- when client with a matching name is opened:
277 require("awful.hotkeys_popup.keys")
278
279 -- {{{ Error handling
280 -- Check if awesome encountered an error during startup and fell back to
281 -- another config (This code will only ever execute for the fallback config)
282 if awesome.startup_errors then
283     naughty.notify({ preset = naughty.config.presets.critical,
284                      title = "Oops, there were errors during startup!",
285                      text = awesome.startup_errors })
286 end
287
288 -- Handle runtime errors after startup
289 do
290     local in_error = false
291     awesome.connect_signal("debug::error", function (err)
292         -- Make sure we don't go into an endless error loop
293         if in_error then return end
294         in_error = true
295
296         naughty.notify({ preset = naughty.config.presets.critical,
297                          title = "Oops, an error happened!",
298                          text = tostring(err) })
299         in_error = false
300     end)
301 end
302 -- }}}
303
304 -- {{{ Variable definitions
305 -- Themes define colours, icons, font and wallpapers.
306 beautiful.init(gears.filesystem.get_themes_dir() .. "default/theme.lua")
307
308 -- This is used later as the default terminal and editor to run.
309 terminal = "kitty"
310 editor = os.getenv("EDITOR") or "nano"
311 editor_cmd = terminal .. editor
312
313 -- Default modkey.
314 -- Usually, Mod4 is the key with a logo between Control and Alt.
315 -- If you do not like this or do not have such a key,
316 -- I suggest you to remap Mod4 to another key using xmodmap or other tools.
317 -- However, you can use another modifier like Mod1, but it may interact with others.
318 modkey = "Mod4"
319
320 -- Table of layouts to cover with awful.layout.inc, order matters.
321 awful.layout.layouts = {
322     awful.layout.suit.floating,
323     awful.layout.suit.tile,
324     awful.layout.suit.tile.left,
325     awful.layout.suit.tile.bottom,
326     awful.layout.suit.tile.top,
327     awful.layout.suit.fair,
328     awful.layout.suit.fair.horizontal,
329     awful.layout.suit.spiral,
330     awful.layout.suit.spiral.dwindle,
331     awful.layout.suit.max,
332     awful.layout.suit.max.fullscreen,
333     awful.layout.suit.magnifier,
334     awful.layout.suit.corner.nw,
335     -- awful.layout.suit.corner.ne,
336     -- awful.layout.suit.corner.sw,
337     -- awful.layout.suit.corner.se,
338 }
339 -- }}}
340
341 -- {{{ Menu
342 -- Create a launcher widget and a main menu
343 myawesomemenu = {
344    { "hotkeys", function() hotkeys_popup.show_help(nil, awful.screen.focused()) end },
345    { "manual", terminal .. " -e man awesome" },
346    { "edit config", editor_cmd .. " " .. awesome.conffile },
347    { "restart", awesome.restart },
348    { "quit", function() awesome.quit() end },
349 }
350
351 mymainmenu = awful.menu({ items = { { "awesome", myawesomemenu, beautiful.awesome_icon },
352                                     { "open terminal", terminal }
353                                   }
354                         })
355
356 mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
357                                      menu = mymainmenu })
358
359 -- Menubar configuration
360 menubar.utils.terminal = terminal -- Set the terminal for applications that require it
361 -- }}}
362
363 -- Keyboard map indicator and switcher
364 mykeyboardlayout = awful.widget.keyboardlayout()
365
366 -- {{{ Wibar
367 -- Create a textclock widget
368 mytextclock = wibox.widget.textclock()
369
370 -- Create a wibox for each screen and add it
371 local taglist_buttons = gears.table.join(
372                     awful.button({ }, 1, function(t) t:view_only() end),
373                     awful.button({ modkey }, 1, function(t)
374                                               if client.focus then
375                                                   client.focus:move_to_tag(t)
376                                               end
377                                           end),
378                     awful.button({ }, 3, awful.tag.viewtoggle),
379                     awful.button({ modkey }, 3, function(t)
380                                               if client.focus then
381                                                   client.focus:toggle_tag(t)
382                                               end
383                                           end),
384                     awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
385                     awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
386                 )
387
388 local tasklist_buttons = gears.table.join(
389                      awful.button({ }, 1, function (c)
390                                               if c == client.focus then
391                                                   c.minimized = true
392                                               else
393                                                   c:emit_signal(
394                                                       "request::activate",
395                                                       "tasklist",
396                                                       {raise = true}
397                                                   )
398                                               end
399                                           end),
400                      awful.button({ }, 3, function()
401                                               awful.menu.client_list({ theme = { width = 250 } })
402                                           end),
403                      awful.button({ }, 4, function ()
404                                               awful.client.focus.byidx(1)
405                                           end),
406                      awful.button({ }, 5, function ()
407                                               awful.client.focus.byidx(-1)
408                                           end))
409
410 local function set_wallpaper(s)
411     -- Wallpaper
412     if beautiful.wallpaper then
413         local wallpaper = beautiful.wallpaper
414         -- If wallpaper is a function, call it with the screen
415         if type(wallpaper) == "function" then
416             wallpaper = wallpaper(s)
417         end
418         gears.wallpaper.maximized(wallpaper, s, true)
419     end
420 end
421
422 -- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
423 screen.connect_signal("property::geometry", set_wallpaper)
424
425 awful.screen.connect_for_each_screen(function(s)
426     -- Wallpaper
427     set_wallpaper(s)
428
429     -- Each screen has its own tag table.
430     awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1])
431
432     -- Create a promptbox for each screen
433     s.mypromptbox = awful.widget.prompt()
434     -- Create an imagebox widget which will contain an icon indicating which layout we're using.
435     -- We need one layoutbox per screen.
436     s.mylayoutbox = awful.widget.layoutbox(s)
437     s.mylayoutbox:buttons(gears.table.join(
438                            awful.button({ }, 1, function () awful.layout.inc( 1) end),
439                            awful.button({ }, 3, function () awful.layout.inc(-1) end),
440                            awful.button({ }, 4, function () awful.layout.inc( 1) end),
441                            awful.button({ }, 5, function () awful.layout.inc(-1) end)))
442     -- Create a taglist widget
443     s.mytaglist = awful.widget.taglist {
444         screen  = s,
445         filter  = awful.widget.taglist.filter.all,
446         buttons = taglist_buttons
447     }
448
449     -- Create a tasklist widget
450     s.mytasklist = awful.widget.tasklist {
451         screen  = s,
452         filter  = awful.widget.tasklist.filter.currenttags,
453         buttons = tasklist_buttons
454     }
455
456     -- Create the wibox
457     s.mywibox = awful.wibar({ position = "top", screen = s })
458
459     -- Add widgets to the wibox
460     s.mywibox:setup {
461         layout = wibox.layout.align.horizontal,
462         { -- Left widgets
463             layout = wibox.layout.fixed.horizontal,
464             mylauncher,
465             s.mytaglist,
466             s.mypromptbox,
467         },
468         s.mytasklist, -- Middle widget
469         { -- Right widgets
470             layout = wibox.layout.fixed.horizontal,
471             mykeyboardlayout,
472             mytextclock,
473             s.mylayoutbox,
474         },
475     }
476 end)
477 -- }}}
478
479 -- {{{ Mouse bindings
480 root.buttons(gears.table.join(
481     awful.button({ }, 3, function () mymainmenu:toggle() end),
482     awful.button({ }, 4, awful.tag.viewnext),
483     awful.button({ }, 5, awful.tag.viewprev)
484 ))
485 -- }}}
486
487 -- {{{ Key bindings
488 globalkeys = gears.table.join(
489     awful.key({ modkey,           }, "s",      hotkeys_popup.show_help,
490               {description="show help", group="awesome"}),
491     awful.key({ modkey,           }, "Left",   awful.tag.viewprev,
492               {description = "view previous", group = "tag"}),
493     awful.key({ modkey,           }, "Right",  awful.tag.viewnext,
494               {description = "view next", group = "tag"}),
495     awful.key({ modkey,           }, "Escape", awful.tag.history.restore,
496               {description = "go back", group = "tag"}),
497
498     awful.key({ modkey,           }, "j",
499         function ()
500             awful.client.focus.byidx( 1)
501         end,
502         {description = "focus next by index", group = "client"}
503     ),
504     awful.key({ modkey,           }, "k",
505         function ()
506             awful.client.focus.byidx(-1)
507         end,
508         {description = "focus previous by index", group = "client"}
509     ),
510     awful.key({ modkey,           }, "w", function () mymainmenu:show() end,
511               {description = "show main menu", group = "awesome"}),
512
513     -- Layout manipulation
514     awful.key({ modkey, "Shift"   }, "j", function () awful.client.swap.byidx(  1)    end,
515               {description = "swap with next client by index", group = "client"}),
516     awful.key({ modkey, "Shift"   }, "k", function () awful.client.swap.byidx( -1)    end,
517               {description = "swap with previous client by index", group = "client"}),
518     awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
519               {description = "focus the next screen", group = "screen"}),
520     awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
521               {description = "focus the previous screen", group = "screen"}),
522     awful.key({ modkey,           }, "u", awful.client.urgent.jumpto,
523               {description = "jump to urgent client", group = "client"}),
524     awful.key({ modkey,           }, "Tab",
525         function ()
526             awful.client.focus.history.previous()
527             if client.focus then
528                 client.focus:raise()
529             end
530         end,
531         {description = "go back", group = "client"}),
532
533     -- Standard program
534     awful.key({ modkey,           }, "Return", function () awful.spawn(terminal) end,
535               {description = "open a termial", group = "launcher"}),
536     awful.key({ modkey, "Control" }, "r", awesome.restart,
537               {description = "reload awesome", group = "awesome"}),
538     awful.key({ modkey, "Shift"   }, "q", awesome.quit,
539               {description = "quit awesome", group = "awesome"}),
540
541     awful.key({ modkey,           }, "l",     function () awful.tag.incmwfact( 0.05)          end,
542               {description = "increase master width factor", group = "layout"}),
543     awful.key({ modkey,           }, "h",     function () awful.tag.incmwfact(-0.05)          end,
544               {description = "decrease master width factor", group = "layout"}),
545     awful.key({ modkey, "Shift"   }, "h",     function () awful.tag.incnmaster( 1, nil, true) end,
546               {description = "increase the number of master clients", group = "layout"}),
547     awful.key({ modkey, "Shift"   }, "l",     function () awful.tag.incnmaster(-1, nil, true) end,
548               {description = "decrease the number of master clients", group = "layout"}),
549     awful.key({ modkey, "Control" }, "h",     function () awful.tag.incncol( 1, nil, true)    end,
550               {description = "increase the number of columns", group = "layout"}),
551     awful.key({ modkey, "Control" }, "l",     function () awful.tag.incncol(-1, nil, true)    end,
552               {description = "decrease the number of columns", group = "layout"}),
553     awful.key({ modkey,           }, "space", function () awful.layout.inc( 1)                end,
554               {description = "select next", group = "layout"}),
555     awful.key({ modkey, "Shift"   }, "space", function () awful.layout.inc(-1)                end,
556               {description = "select previous", group = "layout"}),
557
558     awful.key({ modkey, "Control" }, "n",
559               function ()
560                   local c = awful.client.restore()
561                   -- Focus restored client
562                   if c then
563                     c:emit_signal(
564                         "request::activate", "key.unminimize", {raise = true}
565                     )
566                   end
567               end,
568               {description = "restore minimized", group = "client"}),
569
570     -- Prompt
571     awful.key({ modkey },            "r",     function () awful.screen.focused().mypromptbox:run() end,
572               {description = "run prompt", group = "launcher"}),
573
574     awful.key({ modkey }, "x",
575               function ()
576                   awful.prompt.run {
577                     prompt       = "Run Lua code: ",
578                     textbox      = awful.screen.focused().mypromptbox.widget,
579                     exe_callback = awful.util.eval,
580                     history_path = awful.util.get_cache_dir() .. "/history_eval"
581                   }
582               end,
583               {description = "lua execute prompt", group = "awesome"}),
584     -- Menubar
585     awful.key({ modkey }, "p", function() menubar.show() end,
586               {description = "show the menubar", group = "launcher"})
587 )
588
589 clientkeys = gears.table.join(
590     awful.key({ modkey,           }, "f",
591         function (c)
592             c.fullscreen = not c.fullscreen
593             c:raise()
594         end,
595         {description = "toggle fullscreen", group = "client"}),
596     awful.key({ modkey, "Shift"   }, "c",      function (c) c:kill()                         end,
597               {description = "close", group = "client"}),
598     awful.key({ modkey, "Control" }, "space",  awful.client.floating.toggle                     ,
599               {description = "toggle floating", group = "client"}),
600     awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
601               {description = "move to master", group = "client"}),
602     awful.key({ modkey,           }, "o",      function (c) c:move_to_screen()               end,
603               {description = "move to screen", group = "client"}),
604     awful.key({ modkey,           }, "t",      function (c) c.ontop = not c.ontop            end,
605               {description = "toggle keep on top", group = "client"}),
606     awful.key({ modkey,           }, "n",
607         function (c)
608             -- The client currently has the input focus, so it cannot be
609             -- minimized, since minimized clients can't have the focus.
610             c.minimized = true
611         end ,
612         {description = "minimize", group = "client"}),
613     awful.key({ modkey,           }, "m",
614         function (c)
615             c.maximized = not c.maximized
616             c:raise()
617         end ,
618         {description = "(un)maximize", group = "client"}),
619     awful.key({ modkey, "Control" }, "m",
620         function (c)
621             c.maximized_vertical = not c.maximized_vertical
622             c:raise()
623         end ,
624         {description = "(un)maximize vertically", group = "client"}),
625     awful.key({ modkey, "Shift"   }, "m",
626         function (c)
627             c.maximized_horizontal = not c.maximized_horizontal
628             c:raise()
629         end ,
630         {description = "(un)maximize horizontally", group = "client"})
631 )
632
633 -- Bind all key numbers to tags.
634 -- Be careful: we use keycodes to make it work on any keyboard layout.
635 -- This should map on the top row of your keyboard, usually 1 to 9.
636 for i = 1, 9 do
637     globalkeys = gears.table.join(globalkeys,
638         -- View tag only.
639         awful.key({ modkey }, "#" .. i + 9,
640                   function ()
641                         local screen = awful.screen.focused()
642                         local tag = screen.tags[i]
643                         if tag then
644                            tag:view_only()
645                         end
646                   end,
647                   {description = "view tag #"..i, group = "tag"}),
648         -- Toggle tag display.
649         awful.key({ modkey, "Control" }, "#" .. i + 9,
650                   function ()
651                       local screen = awful.screen.focused()
652                       local tag = screen.tags[i]
653                       if tag then
654                          awful.tag.viewtoggle(tag)
655                       end
656                   end,
657                   {description = "toggle tag #" .. i, group = "tag"}),
658         -- Move client to tag.
659         awful.key({ modkey, "Shift" }, "#" .. i + 9,
660                   function ()
661                       if client.focus then
662                           local tag = client.focus.screen.tags[i]
663                           if tag then
664                               client.focus:move_to_tag(tag)
665                           end
666                      end
667                   end,
668                   {description = "move focused client to tag #"..i, group = "tag"}),
669         -- Toggle tag on focused client.
670         awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
671                   function ()
672                       if client.focus then
673                           local tag = client.focus.screen.tags[i]
674                           if tag then
675                               client.focus:toggle_tag(tag)
676                           end
677                       end
678                   end,
679                   {description = "toggle focused client on tag #" .. i, group = "tag"})
680     )
681 end
682
683 clientbuttons = gears.table.join(
684     awful.button({ }, 1, function (c)
685         c:emit_signal("request::activate", "mouse_click", {raise = true})
686     end),
687     awful.button({ modkey }, 1, function (c)
688         c:emit_signal("request::activate", "mouse_click", {raise = true})
689         awful.mouse.client.move(c)
690     end),
691     awful.button({ modkey }, 3, function (c)
692         c:emit_signal("request::activate", "mouse_click", {raise = true})
693         awful.mouse.client.resize(c)
694     end)
695 )
696
697 -- Set keys
698 root.keys(globalkeys)
699 -- }}}
700
701 -- {{{ Rules
702 -- Rules to apply to new clients (through the "manage" signal).
703 awful.rules.rules = {
704     -- All clients will match this rule.
705     { rule = { },
706       properties = { border_width = beautiful.border_width,
707                      border_color = beautiful.border_normal,
708                      focus = awful.client.focus.filter,
709                      raise = true,
710                      keys = clientkeys,
711                      buttons = clientbuttons,
712                      screen = awful.screen.preferred,
713                      placement = awful.placement.no_overlap+awful.placement.no_offscreen
714      }
715     },
716
717     -- Floating clients.
718     { rule_any = {
719         instance = {
720           "DTA",  -- Firefox addon DownThemAll.
721           "copyq",  -- Includes session name in class.
722           "pinentry",
723         },
724         class = {
725           "Arandr",
726           "Blueman-manager",
727           "Gpick",
728           "Kruler",
729           "MessageWin",  -- kalarm.
730           "Sxiv",
731           "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size.
732           "Wpa_gui",
733           "veromix",
734           "xtightvncviewer"},
735
736         -- Note that the name property shown in xprop might be set slightly after creation of the client
737         -- and the name shown there might not match defined rules here.
738         name = {
739           "Event Tester",  -- xev.
740         },
741         role = {
742           "AlarmWindow",  -- Thunderbird's calendar.
743           "ConfigManager",  -- Thunderbird's about:config.
744           "pop-up",       -- e.g. Google Chrome's (detached) Developer Tools.
745         }
746       }, properties = { floating = true }},
747
748     -- Add titlebars to normal clients and dialogs
749     { rule_any = {type = { "normal", "dialog" }
750       }, properties = { titlebars_enabled = true }
751     },
752
753         { rule = { class = "Xfdesktop" },
754       properties = { sticky = true, border_width = 0, skip_taskbar = true } },
755       { rule = { class = "Xfce4-panel" },
756       properties = { focus = false, raise = false, border_width = 0, type = dock } },
757
758     -- Set Firefox to always map on the tag named "2" on screen 1.
759     -- { rule = { class = "Firefox" },
760     --   properties = { screen = 1, tag = "2" } },
761 }
762 -- }}}
763
764 -- {{{ Signals
765 -- Signal function to execute when a new client appears.
766 client.connect_signal("manage", function (c)
767     -- Set the windows at the slave,
768     -- i.e. put it at the end of others instead of setting it master.
769     -- if not awesome.startup then awful.client.setslave(c) end
770
771     if awesome.startup
772       and not c.size_hints.user_position
773       and not c.size_hints.program_position then
774         -- Prevent clients from being unreachable after screen count changes.
775         awful.placement.no_offscreen(c)
776     end
777 end)
778
779 -- Add a titlebar if titlebars_enabled is set to true in the rules.
780 client.connect_signal("request::titlebars", function(c)
781     -- buttons for the titlebar
782     local buttons = gears.table.join(
783         awful.button({ }, 1, function()
784             c:emit_signal("request::activate", "titlebar", {raise = true})
785             awful.mouse.client.move(c)
786         end),
787         awful.button({ }, 3, function()
788             c:emit_signal("request::activate", "titlebar", {raise = true})
789             awful.mouse.client.resize(c)
790         end)
791     )
792
793     awful.titlebar(c) : setup {
794         { -- Left
795             awful.titlebar.widget.iconwidget(c),
796             buttons = buttons,
797             layout  = wibox.layout.fixed.horizontal
798         },
799         { -- Middle
800             { -- Title
801                 align  = "center",
802                 widget = awful.titlebar.widget.titlewidget(c)
803             },
804             buttons = buttons,
805             layout  = wibox.layout.flex.horizontal
806         },
807         { -- Right
808             awful.titlebar.widget.floatingbutton (c),
809             awful.titlebar.widget.maximizedbutton(c),
810             awful.titlebar.widget.stickybutton   (c),
811             awful.titlebar.widget.ontopbutton    (c),
812             awful.titlebar.widget.closebutton    (c),
813             layout = wibox.layout.fixed.horizontal()
814         },
815         layout = wibox.layout.align.horizontal
816     }
817 end)
818
819 -- Enable sloppy focus, so that focus follows mouse.
820 client.connect_signal("mouse::enter", function(c)
821     c:emit_signal("request::activate", "mouse_enter", {raise = false})
822 end)
823
824 client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
825 client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
826 -- }}}
827
828   '';
829
830   services.picom = {
831     enable = true;
832     vSync = true;
833     opacityRules = [
834       "0:_NET_WM_STATE@[*]:a = '_NET_WM_STATE_HIDDEN'"
835     ];
836   };
837
838   programs.git = {
839     userName = "patrick-scho";
840     userEmail = "patrick.schoenberger@posteo.de";
841     includes = [{ contents = {
842         user = {
843           email = "patrick.schoenberger@posteo.de";
844           name = "psch";
845         };
846     };}];
847   };
848
849   programs.bash = {
850     enable = true;
851     historySize = -1;
852     historyFileSize = -1;
853   };
854
855   programs.fzf = {
856     enable = true;
857     enableBashIntegration = true;
858   };
859
860   programs.readline = {
861     enable = true;
862     bindings = {
863       "\\e[A" = "history-search-backward";
864       "\\e[B" = "history-search-forward";
865     };
866   };
867
868   programs.kitty = {
869     enable = true;
870     themeFile = "Adapta_Nokto_Maia";
871     settings = {
872       # hide_window_decorations = "yes";
873       background_opacity = "0.98";
874       background_blur = "1";
875       confirm_os_window_close = "0";
876       enable_audio_bell = "no";
877       scrollback_pager_history_size = "1024";
878     };
879   };
880
881   programs.helix = {
882     enable = true;
883     settings = {
884       theme = "base16_terminal";
885       editor.cursor-shape = {
886         insert = "bar";
887         normal = "block";
888         select = "underline";
889       };
890       editor.soft-wrap = {
891         enable = true;
892       };
893       editor.file-picker = {
894         hidden = false;
895       };
896       keys.normal."space" = {
897         "space" = "goto_word";
898       };
899     };
900     languages = {
901       language = [{
902         name = "c";
903         auto-format = true;
904         formatter = { command = "clang-format"; args = ["--style=microsoft"]; };
905       }];
906     };
907   };
908
909   home.file.".config/zls.json".text = ''
910     {
911       "enable_build_on_save": true,
912       "build_on_save_step": "check"
913     }
914   '';
915   
916   programs.neovim = {
917     enable = true;
918     defaultEditor = true;
919     plugins = with pkgs.vimPlugins; [
920       # fzfWrapper
921       # fzf-vim
922       formatter-nvim
923       goyo-vim
924       vim-visual-multi
925       nvim-lspconfig
926     ];
927     extraConfig = ''
928       set number
929       set relativenumber
930       set tabstop=4
931       set shiftwidth=4
932       set foldmethod=marker
933       colorscheme habamax
934
935       nnoremap <Down> gj
936       nnoremap <Up> gk
937       vnoremap <Down> gj
938       vnoremap <Up> gk
939       inoremap <Down> <C-o>gj
940       inoremap <Up> <C-o>gk
941       tnoremap <Esc> <C-\><C-n>
942     '';
943     extraLuaConfig = ''
944       require('lspconfig').zls.setup{}
945       require('formatter').setup {
946         
947       }
948     '';
949   };
950
951
952   home.file.".config/vis/plugins/vis-lspc" = {
953     source = builtins.fetchGit {
954       url = "https://gitlab.com/muhq/vis-lspc.git";
955       rev = "e184eb6c971abfcd0dc7f836f402aae6c33a8d13";
956     };
957     recursive = true;
958   };
959   home.file.".config/vis/plugins/vis-commentary" = {
960     source = builtins.fetchGit {
961       url = "https://github.com/lutobler/vis-commentary.git";
962       rev = "0e06ed8212c12c6651cb078b822390094b396f08";
963     };
964     recursive = true;
965   };
966   home.file.".config/vis/visrc.lua".text = ''
967       require('vis')
968       require('plugins/vis-commentary')
969       lspc = require('plugins/vis-lspc')
970
971       lspc.menu_cmd = 'vis-menu'
972       lspc.fallback_dirname_as_root = true
973       lspc.ls_map.zig = {name='zls',cmd='zls',roots={'build.zig'}}
974     
975       vis.events.subscribe(vis.events.INIT, function()
976         -- Your global configuration options
977       end)
978
979       vis.events.subscribe(vis.events.WIN_OPEN, function(win) -- luacheck: no unused args
980         -- Your per window configuration options e.g.
981         vis:command('set number on')
982         vis:command('set autoindent')
983         vis:command('set tabwidth 4')
984         vis:command('set expandtab on')
985       end)
986     '';
987
988   programs.emacs = {
989     enable = true;
990     extraConfig = ''
991       (menu-bar-mode 0)
992       (tool-bar-mode 0)
993       (scroll-bar-mode 0)
994       (global-display-line-numbers-mode)
995       (setq-default
996         display-line-numbers-type 'relative
997         make-backup-files nil
998         inhibit-startup-screen t)
999       (load-theme 'tango-dark t)
1000     '';
1001     extraPackages = epkgs: [
1002       epkgs.zig-mode
1003       epkgs.lsp-mode
1004     ];
1005   };
1006
1007   programs.tmux = {
1008     enable = true;
1009     escapeTime = 0;
1010     extraConfig = ''
1011       set-option -g default-terminal screen-256color
1012       set -g history-limit 10000
1013       set -g base-index 1
1014       set-option -g renumber-windows on
1015       bind-key -n M-n new-window -c "#{pane_current_path}"
1016       bind-key -n M-1 select-window -t :1
1017       bind-key -n M-2 select-window -t :2
1018       bind-key -n M-3 select-window -t :3
1019       bind-key -n M-4 select-window -t :4
1020       bind-key -n M-5 select-window -t :5
1021       bind-key -n M-6 select-window -t :6
1022       bind-key -n M-7 select-window -t :7
1023       bind-key -n M-8 select-window -t :8
1024       bind-key -n M-9 select-window -t :9
1025       bind-key -n M-0 select-window -t :0
1026       bind-key -n M-. select-window -n
1027       bind-key -n M-, select-window -p
1028       bind-key -n M-S-. swap-window -t +1
1029       bind-key -n M-S-, swap-window -t -1
1030       bind-key -n M-X confirm-before "kill-window"
1031       bind-key -n M-v split-window -h -c "#{pane_current_path}"
1032       bind-key -n M-b split-window -v -c "#{pane_current_path}"
1033       bind-key -n M-R command-prompt -I "" "rename-window '%%'"
1034       bind-key -n M-f resize-pane -Z
1035       bind-key -n M-h select-pane -L
1036       bind-key -n M-l select-pane -R
1037       bind-key -n M-k select-pane -U
1038       bind-key -n M-j select-pane -D
1039       bind-key -n M-Left select-pane -L
1040       bind-key -n M-Right select-pane -R
1041       bind-key -n M-Up select-pane -U
1042       bind-key -n M-Down select-pane -D
1043       bind-key -n "M-H" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -L; tmux swap-pane -t $old'
1044       bind-key -n "M-J" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -D; tmux swap-pane -t $old'
1045       bind-key -n "M-K" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -U; tmux swap-pane -t $old'
1046       bind-key -n "M-L" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -R; tmux swap-pane -t $old'
1047       bind-key -n "M-S-Left" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -L; tmux swap-pane -t $old'
1048       bind-key -n "M-S-Down" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -D; tmux swap-pane -t $old'
1049       bind-key -n "M-S-Up" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -U; tmux swap-pane -t $old'
1050       bind-key -n "M-S-Right" run-shell 'old=`tmux display -p "#{pane_index}"`; tmux select-pane -R; tmux swap-pane -t $old'
1051       bind-key -n M-x confirm-before "kill-pane"
1052       bind-key -n M-/ copy-mode
1053
1054       # Linux system clipboard
1055       bind -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "xclip -in -selection clipboard"
1056       bind-key -T copy-mode-vi MouseDragEnd1Pane send -X copy-pipe-and-cancel "xclip -in -selection clipboard"
1057
1058       set -g mouse on
1059       # set-option -g status-keys vi
1060       # set-option -g set-titles on
1061       # set-option -g set-titles-string 'tmux - #W'
1062       # set -g bell-action any
1063       # set-option -g visual-bell off
1064       # set-option -g set-clipboard off
1065       # setw -g mode-keys vi
1066       # setw -g monitor-activity on
1067       # set -g visual-activity on
1068       # set -g status-style fg=colour15
1069       # set -g status-justify centre
1070       # set -g status-left ""
1071       # set -g status-right ""
1072       # set -g status-interval 1
1073       # set -g message-style fg=colour0,bg=colour3
1074       # setw -g window-status-bell-style fg=colour1
1075       # setw -g window-status-current-style fg=yellow,bold
1076       # setw -g window-status-style fg=colour250
1077       # setw -g window-status-current-format ' #{?#{==:#W,#{b:SHELL}},#{b:pane_current_path},#W} '
1078       # setw -g window-status-format ' #{?#{==:#W,#{b:SHELL}},#{b:pane_current_path},#W} '
1079       # # For older tmux:
1080       # #setw -g window-status-format ' #W '
1081       # #setw -g window-status-current-format ' #W '
1082     '';
1083   };
1084
1085   programs.nushell = {
1086     enable = true;
1087     # for editing directly to config.nu 
1088     extraConfig = ''
1089       $env.config = {
1090         show_banner: false,
1091         completions: {
1092           case_sensitive: false # case-sensitive completions
1093           quick: true           # set to false to prevent auto-selecting completions
1094           partial: true         # set to false to prevent partial filling of the prompt
1095           algorithm: "fuzzy"    # prefix or fuzzy
1096         }
1097         hooks: {
1098           command_not_found: { |cmd| (command-not-found $cmd | str trim)  }
1099         }
1100       } 
1101     '';
1102     #  shellAliases = {
1103     #  vi = "hx";
1104     #  vim = "hx";
1105     #  nano = "hx";
1106     # };
1107   };  
1108   # programs.carapace = {
1109   #   enable = true;
1110   #   enableNushellIntegration = true;
1111   # };
1112
1113   # programs.starship = {
1114   #   enable = true;
1115   #   settings = {
1116   #     add_newline = false;
1117   #     character = { 
1118   #       success_symbol = "[➜](bold green)";
1119   #       error_symbol = "[➜](bold red)";
1120   #     };
1121   #   };
1122   # };
1123
1124   programs.firefox = {
1125     enable = true;
1126     profiles = {
1127       default = {
1128         settings = {
1129           "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
1130           "browser.fullscreen.autohide" = false;
1131           "browser.toolbars.bookmarks.visibility" = "never";
1132           "browser.tabs.inTitlebar" = 0;
1133           "browser.compactmode.show" = true;
1134           "browser.uidensity" = 1;
1135         };
1136         userChrome = ''
1137         #TabsToolbar {
1138           visibility: collapse !important;
1139         }
1140         '';
1141       };
1142       appmode = {
1143         id = 1;
1144         settings = {
1145           "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
1146           "browser.fullscreen.autohide" = false;
1147           "browser.toolbars.bookmarks.visibility" = "never";
1148           "browser.tabs.inTitlebar" = 0;
1149           "browser.compactmode.show" = true;
1150           "browser.uidensity" = 1;
1151         };
1152         userChrome = ''
1153         #TabsToolbar {
1154           visibility: collapse !important;
1155         }
1156         #nav-bar {
1157           visibility: collapse !important;
1158         }
1159         #navigator-toolbox {
1160           border-bottom: none !important;
1161         }
1162         '';        
1163       };
1164     };
1165   };
1166
1167   xdg.desktopEntries = {
1168     whatsapp = {
1169       name = "WhatsApp";
1170       genericName = "Messenger";
1171       exec = "app web.whatsapp.com";
1172       terminal = false;
1173       categories = [ "Application" ];
1174       icon = pkgs.fetchurl {
1175         url = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/2062095_application_chat_communication_logo_whatsapp_icon.svg/1024px-2062095_application_chat_communication_logo_whatsapp_icon.svg.png";
1176         sha256 = "sha256-0eE3EEGnWFlpObfraTXMIqJz0Uya/h0hDsUA528qKCY=";
1177       };
1178     };
1179     md = {
1180       name = "Markdown";
1181       genericName = "Documents";
1182       exec = "md-app";
1183       terminal = false;
1184       categories = [ "Application" ];
1185       icon = pkgs.fetchurl {
1186         url = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/Markdown-mark.svg/1024px-Markdown-mark.svg.png";
1187         sha256 = "0v161jvmcfxp9lwd86y789430w1vpvxnnm5n2hzgr1kfh03psvb2";
1188       };
1189     };
1190   };
1191
1192   gtk.enable = true;
1193   gtk.theme = {
1194     package = theme;
1195     name = "Qogir-Dark";
1196   };
1197   # programs.gnome-shell.theme = {
1198   #   package = theme;
1199   #   name = "Qogir-Dark";
1200   # };
1201   # gtk.iconTheme = {
1202   #   package = pkgs.vimix-icon-theme;
1203   #   name = "vimix-doder-dark";
1204   # };
1205   home.pointerCursor = {
1206     gtk.enable = true;
1207     x11.enable = true;
1208
1209     name = "volantes_light_cursors";
1210     size = 24;
1211     package = pkgs.volantes-cursors;
1212   };
1213
1214   # This value determines the Home Manager release that your configuration is
1215   # compatible with. This helps avoid breakage when a new Home Manager release
1216   # introduces backwards incompatible changes.
1217   #
1218   # You should not change this value, even if you update Home Manager. If you do
1219   # want to update the value, then make sure to first check the Home Manager
1220   # release notes.
1221   home.stateVersion = "24.05"; # Please read the comment before changing.
1222
1223   # The home.packages option allows you to install Nix packages into your
1224   # environment.
1225   home.packages = with pkgs; [
1226     nixos-icons
1227     spotify-player
1228     prismlauncher
1229     pandoc
1230     ghidra
1231     gnome-firmware
1232     fzf bat delta silver-searcher ripgrep perl universal-ctags
1233     # bintools
1234     htop
1235     unzip
1236     rr wrk
1237     clang-tools bear
1238     linuxPackages_latest.perf
1239     texliveFull
1240     emscripten
1241     caddy
1242     python3
1243     qogir-icon-theme
1244     volantes-cursors
1245     xorg.xkill
1246     lf nnn
1247     feh
1248     xarchiver
1249     tig lazygit gitui
1250     thunderbird
1251     libreoffice
1252     gimp
1253     guvcview
1254     arandr
1255     vial
1256     ncdu
1257     gnumake gcc
1258     linux-wifi-hotspot
1259     esptool picocom
1260     wireshark
1261     nil
1262     bc
1263     ffmpeg
1264     sc-im visidata
1265     localsend
1266     vis
1267     wineWowPackages.unstableFull winetricks
1268
1269     # # It is sometimes useful to fine-tune packages, for example, by applying
1270     # # overrides. You can do that directly here, just don't forget the
1271     # # parentheses. Maybe you want to install Nerd Fonts with a limited number of
1272     # # fonts?
1273     # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
1274
1275     # # You can also create simple shell scripts directly inside your
1276     # # configuration. For example, this adds a command 'my-hello' to your
1277     # # environment:
1278     # (pkgs.writeShellScriptBin "my-hello" ''
1279     #   echo "Hello, ${config.home.username}!"
1280     # '')
1281     # $@ - Liste aller Parameter
1282     # $* - String aller Parameter
1283     # $# - Anzahl aller Parameter
1284     # &> - stdout und stderr pipen
1285     # '' - Literal
1286     # "" - Expanded
1287     # 
1288     (pkgs.writeShellScriptBin "snrs" ''
1289       sudo nixos-rebuild switch --flake /etc/nixos#default
1290     '')
1291     (pkgs.writeShellScriptBin "snrt" ''
1292       sudo nixos-rebuild test --flake /etc/nixos#default
1293     '')
1294     (pkgs.writeShellScriptBin "snrb" ''
1295       sudo nixos-rebuild boot --flake /etc/nixos#default
1296     '')
1297     (pkgs.writeShellScriptBin "senc" ''
1298       sudo ${pkgs.helix}/bin/hx /etc/nixos/configuration.nix
1299     '')
1300     (pkgs.writeShellScriptBin "senh" ''
1301       sudo ${pkgs.helix}/bin/hx /etc/nixos/home.nix
1302     '')
1303     
1304     (pkgs.writeShellScriptBin "app" ''
1305       ${pkgs.firefox}/bin/firefox -p appmode --new-window "$@"
1306     '')
1307     (pkgs.writeShellScriptBin "md" ''
1308       file=$(mktemp --suffix=.html) && \
1309       echo $file && \
1310       ${pkgs.pandoc}/bin/pandoc -o $file $1 -s -H ${markdownStyleHeader} && \
1311       app $file && \
1312       rm $file
1313     '')
1314     (pkgs.writeShellScriptBin "run" ''
1315       (nohup "$@" &>/dev/null &); sleep 0
1316     '')
1317     (pkgs.writeShellScriptBin "popup" ''
1318       ${pkgs.kitty}/bin/kitty -o hide_window_decorations=yes -o background_opacity=0.5 \
1319         --class kitty-popup -o remember_window_size=no \
1320         -o initial_window_width=120c -o initial_window_height=40c \
1321         "$@"
1322     '')
1323     (pkgs.writeShellScriptBin "fzfdir" ''
1324       find "$1" -name "$2" | ${pkgs.fzf}/bin/fzf --layout=reverse
1325     '')
1326     (pkgs.writeShellScriptBin "md-app" ''
1327       #popup bash -c 'file=$(fzfdir "md" "*.md") && run md $file'
1328       firefox -p appmode --new-window localhost:8123/home/ps/sync/txt/hsrm
1329     '')
1330     (pkgs.writeShellScriptBin "run-popup" ''
1331       popup bash -c 'file=$(compgen -c | grep -v fzf | sort -u | fzf --layout=reverse --print-query | tail -n 1) && run $file'
1332     '')
1333     (pkgs.writeShellScriptBin "set-wallpaper" ''
1334       ${pkgs.feh}/bin/feh --bg-fill --no-fehbg ${wallpaper}
1335     '')
1336   ];
1337
1338   # Home Manager is pretty good at managing dotfiles. The primary way to manage
1339   # plain files is through 'home.file'.
1340   home.file = {
1341     # # Building this configuration will create a copy of 'dotfiles/screenrc' in
1342     # # the Nix store. Activating the configuration will then make '~/.screenrc' a
1343     # # symlink to the Nix store copy.
1344     # ".screenrc".source = dotfiles/screenrc;
1345
1346     # # You can also set the file content immediately.
1347     # ".gradle/gradle.properties".text = ''
1348     #   org.gradle.console=verbose
1349     #   org.gradle.daemon.idletimeout=3600000
1350     # '';
1351   };
1352
1353   # Home Manager can also manage your environment variables through
1354   # 'home.sessionVariables'. These will be explicitly sourced when using a
1355   # shell provided by Home Manager. If you don't want to manage your shell
1356   # through Home Manager then you have to manually source 'hm-session-vars.sh'
1357   # located at either
1358   #
1359   #  ~/.nix-profile/etc/profile.d/hm-session-vars.sh
1360   #
1361   # or
1362   #
1363   #  ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
1364   #
1365   # or
1366   #
1367   #  /etc/profiles/per-user/ps/etc/profile.d/hm-session-vars.sh
1368   #
1369   home.sessionVariables = {
1370     # EDITOR = "emacs";
1371   };
1372
1373   # Let Home Manager install and manage itself.
1374   programs.home-manager.enable = true;
1375 }