commit 563eed2cc73d85126da654a26d09bfffeda5e67f Author: Mikael Voss Date: Tue Jul 30 13:55:58 2024 +0200 Initial import diff --git a/firefox.nix b/firefox.nix new file mode 100644 index 0000000..c6ea3e7 --- /dev/null +++ b/firefox.nix @@ -0,0 +1,251 @@ +{ config, lib, pkgs, osConfig, ... }: +let + inherit (osConfig) hardware; + + graphical = + if lib.versionAtLeast osConfig.system.stateVersion "24.11" + then hardware.graphic.enable + else hardware.opengl.enable; + + firefox-csshacks = pkgs.fetchFromGitHub { + owner = "MrOtherGuy"; + repo = "firefox-csshacks"; + rev = "7eca4b1050c4065130a2cf696302b4ef5d88d932"; + sparseCheckout = [ "!/*" "/chrome" "/content" ]; + hash = "sha256-rk0jC5AMw41xt5yItY7CAxuYAFhoe5Jy2tvwgh59cPI="; + }; +in lib.mkIf graphical { + programs.firefox = { + enable = true; + package = pkgs.firefox; + profiles = let + extensions = with config.nur.repos.rycee.firefox-addons; [ + clearurls + consent-o-matic + decentraleyes + keepassxc-browser + multi-account-containers + ublock-origin + ]; + settings = { + # use OS locale + "intl.regional_prefs.use_os_locales" = true; + + # localisation + "intl.accept_languages" = "en-gb,en,de,fr,es-es,es,pt,ja"; + "intl.locale.requested" = "en-GB,en,de,fr,es-ES,es,pt,ja"; + + # use OS resolver + "network.trr.mode" = 5; + + # force HTTPS + "dom.security.https_only_mode" = true; + "dom.security.https_only_mode_ever_enabled" = true; + + # enable EME + "media.eme.enabled" = true; + + # founts + "font.default.x-unicode" = "sans-serif"; + "font.default.x-western" = "sans-serif"; + "font.name.sans-serif.x-unicode" = "Lato"; + "font.name.sans-serif.x-western" = "Lato"; + "font.name.monospace.x-unicode" = "Fira Code"; + "font.name.monospace.x-western" = "Fira Code"; + + # hardware acceleration + "gfx.webrender.all" = true; + "layers.acceleration.force-enabled" = true; + "media.ffmpeg.vaapi.enabled" = true; + + # always ask for download location + "browser.download.useDownloadDir" = false; + + # disable firefox tab + "browser.tabs.firefox-view" = false; + + # disable firefox intro tab + "browser.startup.homepage_override.mstone" = "ignore"; + + # disable default browser check + "browser.shell.checkDefaultBrowser" = false; + + # private containor for new tab page thumbnails + "privacy.usercontext.about_newtab_segregation.enabled" = true; + + # disable Beacons API + "beacon.enabled" = false; + + # disable pings + "browser.send_pings" = false; + + # strip query parameters + "privacy.query_stripping" = true; + + # disable access to device sensors + "device.sensors.enabled" = false; + "dom.battery.enabled" = false; + + # disable media auto‐play + "media.autoplay.enabled" = false; + + # block third‐party cookies + "network.cookie.cookieBehavior" = 1; + + # spoof referrer header + "network.http.referer.spoofSource" = true; + + # isolate all browser identifier sources + "privacy.firstparty.isolate" = true; + + # resist fingerprinting + #"privacy.resistFingerprinting" = true; + + # enable built‐in tracking protection + "privacy.trackingprotection.enabled" = true; + "privacy.trackingprotection.emailtracking.enabled" = true; + "privacy.trackingprotection.socialtracking.enabled" = true; + + # disable data sharing + "app.normandy.enabled" = false; + "app.shield.optoutstudies.enabled" = false; + "datareporting.healthreport.uploadEnabled" = false; + + # disable safebrowsing + "browser.safebrowsing.downloads.enabled" = false; + "browser.safebrowsing.malware.enabled" = false; + "browser.safebrowsing.phishing.enabled" = false; + + # disable firefox account + "identity.fxaccounts.enabled" = false; + + # disable sponsored items + "browser.newtabpage.activity-stream.showSponsoredTopSites" = false; + "browser.newtabpage.enhanced" = false; + + # disable Pocket + "extensions.pocket.enabled" = false; + + # disable crash reporting + "browser.tabs.crashReporting.sendReport" = false; + "breakpad.reportURL" = ""; + + # disable accessibility services + "accessibility.force_disabled" = true; + + # disable password auto‐fill + "signon.autofillForms" = false; + + # enable user profile customisation + "toolkit.legacyUserProfileCustomizations.stylesheets" = true; + }; + userChrome = lib.concatMapStrings (css: + "@import url('${firefox-csshacks}/chrome/${css}.css');\n" + ) [ + "hide_tabs_with_one_tab" + "autohide_bookmarks_and_main_toolbars" + ]; + search = { + default = "Google Search"; + force = true; + engines = { + "Google Search" = { + urls = [{ template = "https://www.google.com/search?q={searchTerms}"; }]; + definedAliases = [ "g" ]; + }; + + "Nix Packages" = { + urls = [{ + template = "https://search.nixos.org/packages"; + params = [ + { name = "channel"; value = "unstable"; } + { name = "type"; value = "packages"; } + { name = "query"; value = "{searchTerms}"; } + ]; + }]; + + definedAliases = [ "np" ]; + }; + + "Gentoo Packages" = { + urls = [{ template = "https://packages.gentoo.org/packages/search?q={searchTerms}"; }]; + definedAliases = [ "gp" ]; + }; + + "Alpine Packages" = { + urls = [{ template = "https://pkgs.alpinelinux.org/packages?name={searchTerms}"; }]; + definedAliases = [ "ap" ]; + }; + + "NixOS Wiki" = { + urls = [{ template = "https://nixos.wiki/index.php?search={searchTerms}"; }]; + definedAliases = [ "nw" ]; + }; + + "Wikipedia (eng)" = { + urls = [{ template = "https://en.wikipedia.org/wiki/Special:Search?search={searchTerms}"; }]; + definedAliases = [ "w" ]; + }; + + "Wikipedia (deu)" = { + urls = [{ template = "https://de.wikipedia.org/wiki/Spezial:Suche?search={searchTerms}"; }]; + definedAliases = [ "wd" ]; + }; + + "Wiktionary (eng)" = { + urls = [{ template = "https://en.wiktionary.org/wiki/Special:Search?search={searchTerms}"; }]; + definedAliases = [ "k" ]; + }; + + "Wiktionary (deu)" = { + urls = [{ template = "https://de.wiktionary.org/wiki/Spezial:Suche?search={searchTerms}"; }]; + definedAliases = [ "kd" ]; + }; + + "Linguee (en‐de)" = { + urls = [{ template = "https://www.linguee.com/english-german/search?query={searchTerms}"; }]; + definedAliases = [ "en" ]; + }; + + "Linguee (en‐fr)" = { + urls = [{ template = "https://www.linguee.com/english-french/search?query={searchTerms}"; }]; + definedAliases = [ "fr" ]; + }; + + "Linguee (en‐es)" = { + urls = [{ template = "https://www.linguee.com/english-spanish/search?query={searchTerms}"; }]; + definedAliases = [ "es" ]; + }; + + "Linguee (en‐pt)" = { + urls = [{ template = "https://www.linguee.com/english-portuguese/search?query={searchTerms}"; }]; + definedAliases = [ "pt" ]; + }; + + "Jisho" = { + urls = [{ template = "https://jisho.org/search/{searchTerms}"; }]; + definedAliases = [ "ja" ]; + }; + + "DeepL" = { + urls = [{ template = "https://www.deepl.com/translator#en//{searchTerms}"; }]; + definedAliases = [ "dpl" ]; + }; + }; + }; + in { + default = { + inherit extensions settings userChrome search; + isDefault = true; + }; + sneaky = { + inherit extensions settings userChrome search; + id = 1; + }; + vanilla = { + inherit userChrome search; + id = 2; + }; + }; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..bba596f --- /dev/null +++ b/flake.lock @@ -0,0 +1,314 @@ +{ + "nodes": { + "base16": { + "inputs": { + "fromYaml": "fromYaml" + }, + "locked": { + "lastModified": 1708890466, + "narHash": "sha256-LlrC09LoPi8OPYOGPXegD72v+//VapgAqhbOFS3i8sc=", + "owner": "SenchoPens", + "repo": "base16.nix", + "rev": "665b3c6748534eb766c777298721cece9453fdae", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "base16.nix", + "type": "github" + } + }, + "base16-fish": { + "flake": false, + "locked": { + "lastModified": 1622559957, + "narHash": "sha256-PebymhVYbL8trDVVXxCvZgc0S5VxI7I1Hv4RMSquTpA=", + "owner": "tomyun", + "repo": "base16-fish", + "rev": "2f6dd973a9075dabccd26f1cded09508180bf5fe", + "type": "github" + }, + "original": { + "owner": "tomyun", + "repo": "base16-fish", + "type": "github" + } + }, + "base16-foot": { + "flake": false, + "locked": { + "lastModified": 1696725948, + "narHash": "sha256-65bz2bUL/yzZ1c8/GQASnoiGwaF8DczlxJtzik1c0AU=", + "owner": "tinted-theming", + "repo": "base16-foot", + "rev": "eedbcfa30de0a4baa03e99f5e3ceb5535c2755ce", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-foot", + "type": "github" + } + }, + "base16-helix": { + "flake": false, + "locked": { + "lastModified": 1720809814, + "narHash": "sha256-numb3xigRGnr/deF7wdjBwVg7fpbTH7reFDkJ75AJkY=", + "owner": "tinted-theming", + "repo": "base16-helix", + "rev": "34f41987bec14c0f3f6b2155c19787b1f6489625", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-helix", + "type": "github" + } + }, + "base16-kitty": { + "flake": false, + "locked": { + "lastModified": 1665001328, + "narHash": "sha256-aRaizTYPpuWEcvoYE9U+YRX+Wsc8+iG0guQJbvxEdJY=", + "owner": "kdrag0n", + "repo": "base16-kitty", + "rev": "06bb401fa9a0ffb84365905ffbb959ae5bf40805", + "type": "github" + }, + "original": { + "owner": "kdrag0n", + "repo": "base16-kitty", + "type": "github" + } + }, + "base16-tmux": { + "flake": false, + "locked": { + "lastModified": 1696725902, + "narHash": "sha256-wDPg5elZPcQpu7Df0lI5O8Jv4A3T6jUQIVg63KDU+3Q=", + "owner": "tinted-theming", + "repo": "base16-tmux", + "rev": "c02050bebb60dbb20cb433cd4d8ce668ecc11ba7", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-tmux", + "type": "github" + } + }, + "base16-vim": { + "flake": false, + "locked": { + "lastModified": 1716150083, + "narHash": "sha256-ZMhnNmw34ogE5rJZrjRv5MtG3WaqKd60ds2VXvT6hEc=", + "owner": "tinted-theming", + "repo": "base16-vim", + "rev": "6e955d704d046b0dc3e5c2d68a2a6eeffd2b5d3d", + "type": "github" + }, + "original": { + "owner": "tinted-theming", + "repo": "base16-vim", + "type": "github" + } + }, + "en_EU": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1722354766, + "narHash": "sha256-eKlTozf7vrmsNGwNFWOn+epavcvCyRqK94ZgV9vKe1A=", + "ref": "refs/heads/main", + "rev": "733ceccbfc70345610573334612e6589688d4f5d", + "revCount": 5, + "type": "git", + "url": "https://woof.rip/mikael/en_EU.git" + }, + "original": { + "type": "git", + "url": "https://woof.rip/mikael/en_EU.git" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "fromYaml": { + "flake": false, + "locked": { + "lastModified": 1689549921, + "narHash": "sha256-iX0pk/uB019TdBGlaJEWvBCfydT6sRq+eDcGPifVsCM=", + "owner": "SenchoPens", + "repo": "fromYaml", + "rev": "11fbbbfb32e3289d3c631e0134a23854e7865c84", + "type": "github" + }, + "original": { + "owner": "SenchoPens", + "repo": "fromYaml", + "type": "github" + } + }, + "gnome-shell": { + "flake": false, + "locked": { + "lastModified": 1713702291, + "narHash": "sha256-zYP1ehjtcV8fo+c+JFfkAqktZ384Y+y779fzmR9lQAU=", + "owner": "GNOME", + "repo": "gnome-shell", + "rev": "0d0aadf013f78a7f7f1dc984d0d812971864b934", + "type": "github" + }, + "original": { + "owner": "GNOME", + "ref": "46.1", + "repo": "gnome-shell", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1722321190, + "narHash": "sha256-WeVWVRqkgrbLzmk6FfJoloJ7Xe7HWD27Pv950IUG2kI=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "4fcd54df7cbb1d79cbe81209909ee8514d6b17a4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nix-index-database": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1722136042, + "narHash": "sha256-x3FmT4QSyK28itMiR5zfYhUrG5nY+2dv+AIcKfmSp5A=", + "owner": "nix-community", + "repo": "nix-index-database", + "rev": "c0ca47e8523b578464014961059999d8eddd4aae", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-index-database", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1722141560, + "narHash": "sha256-Ul3rIdesWaiW56PS/Ak3UlJdkwBrD4UcagCmXZR9Z7Y=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "038fb464fcfa79b4f08131b07f2d8c9a6bcc4160", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1722141560, + "narHash": "sha256-Ul3rIdesWaiW56PS/Ak3UlJdkwBrD4UcagCmXZR9Z7Y=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "038fb464fcfa79b4f08131b07f2d8c9a6bcc4160", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nur": { + "locked": { + "lastModified": 1722350417, + "narHash": "sha256-1MNpE3S9W7F1+2wg1WonX1+55c4j0WKFRPYq8JD7WxU=", + "owner": "nix-community", + "repo": "NUR", + "rev": "51808228e58e636d4b59228ed3881f7f971e7bf1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "NUR", + "type": "github" + } + }, + "root": { + "inputs": { + "en_EU": "en_EU", + "home-manager": "home-manager", + "nix-index-database": "nix-index-database", + "nixpkgs": "nixpkgs_2", + "nur": "nur", + "stylix": "stylix" + } + }, + "stylix": { + "inputs": { + "base16": "base16", + "base16-fish": "base16-fish", + "base16-foot": "base16-foot", + "base16-helix": "base16-helix", + "base16-kitty": "base16-kitty", + "base16-tmux": "base16-tmux", + "base16-vim": "base16-vim", + "flake-compat": "flake-compat", + "gnome-shell": "gnome-shell", + "home-manager": [ + "home-manager" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1722295291, + "narHash": "sha256-3XpT9GMw50NCGT1Gd2YAwEjrEcFtDqnuQ7sRUcuU/Pc=", + "owner": "danth", + "repo": "stylix", + "rev": "feb2973dfa8232c07efbd2b48f11a5cfa2276570", + "type": "github" + }, + "original": { + "owner": "danth", + "repo": "stylix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..53d5860 --- /dev/null +++ b/flake.nix @@ -0,0 +1,29 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + nur.url = "github:nix-community/NUR"; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + en_EU.url = "git+https://woof.rip/mikael/en_EU.git"; + + stylix = { + url = "github:danth/stylix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; + }; + + nix-index-database = { + url = "github:nix-community/nix-index-database"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, ... }@inputs: { + nixosModules.default = import ./module.nix inputs; + homeConfigurations.default = import ./home.nix inputs; + }; +} diff --git a/greedy.xkb b/greedy.xkb new file mode 100644 index 0000000..927e79c --- /dev/null +++ b/greedy.xkb @@ -0,0 +1,73 @@ +xkb_symbols "greedy" { + name[Group1]= "Greedy"; + + // Modifier keys + include "ctrl(nocaps)" + include "altwin(alt_super_win)" + include "level3(ralt_switch)" + include "level5(rctrl_switch)" + + include "compose(lwin-altgr)" + include "compose(102)" + include "nbsp(level3n)" + include "keypad(future)" + + key { [ Escape ] }; + key { [ Tab ] }; + + key { [ dollar, asciitilde, EuroSign, dead_tilde ] }; + key { [ ampersand, percent, dead_breve, dead_caron ] }; + key { [ bracketleft, 7, 0x100201E, 0x100201A ] }; + key { [ braceleft, 5, 0x100201C, 0x1002018 ] }; + key { [ braceright, 3, 0x100201D, 0x1002019 ] }; + key { [ parenleft, 1, 0x1002039, NoSymbol ] }; + key { [ equal, 9, 0x1002260, NoSymbol ] }; + key { [ asterisk, 0, 0x10022C5, NoSymbol ] }; + key { [ parenright, 2, 0x100203A, NoSymbol ] }; + key { [ plus, 4, plusminus, NoSymbol ] }; + key { [ bracketright, 6, endash, emdash ] }; + key { [ exclam, 8, exclamdown, infinity ] }; + key { [ numbersign, grave, numerosign, dead_grave ] }; + + key { [ k, K, odiaeresis, Odiaeresis ] }; + key { [ comma, less, dead_cedilla, guillemotleft ] }; + key { [ u, U, oacute, Oacute ] }; + key { [ y, Y, udiaeresis, Udiaeresis ] }; + key { [ p, P, NoSymbol, NoSymbol ] }; + key { [ w, W, NoSymbol, NoSymbol ] }; + key { [ l, L, NoSymbol, NoSymbol ] }; + key { [ m, M, mu, NoSymbol ] }; + key { [ f, F, NoSymbol, NoSymbol ] }; + key { [ c, C, copyright, NoSymbol ] }; + key { [ slash, question, division, questiondown ] }; + key { [ at, asciicircum, 0x100203D, dead_circumflex ] }; + + key { [ o, O, oacute, Oacute ] }; + key { [ a, A, aacute, Aacute ] }; + key { [ e, E, eacute, Eacute ] }; + key { [ i, I, iacute, Iacute ] }; + key { [ d, D, eth, ETH ] }; + key { [ r, R, NoSymbol, NoSymbol ] }; + key { [ n, N, ntilde, Ntilde ] }; + key { [ t, T, thorn, Thorn ] }; + key { [ h, H, NoSymbol, NoSymbol ] }; + key { [ s, S, ssharp, section ] }; + key { [ minus, underscore, 0x1002010, dead_macron ] }; + key { [ backslash, bar, NoSymbol, NoSymbol ] }; + + key { [ q, Q, adiaeresis, Adiaeresis ] }; + key { [ period, greater, ellipsis, guillemotright ] }; + key { [ apostrophe, quotedbl, dead_acute, dead_diaeresis ] }; + key { [ semicolon, colon, periodcentered, NoSymbol ] }; + key { [ z, Z, NoSymbol, NoSymbol ] }; + key { [ x, X, multiply, NoSymbol ] }; + key { [ v, V, NoSymbol, NoSymbol ] }; + key { [ g, G, NoSymbol, NoSymbol ] }; + key { [ b, B, NoSymbol, NoSymbol ] }; + key { [ j, J, NoSymbol, NoSymbol ] }; + + key { [ Up, NoSymbol, uparrow, 0x10021D1 ] }; + key { [ Left, NoSymbol, leftarrow, 0x10021D0 ] }; + key { [ Down, NoSymbol, downarrow, 0x10021D3 ] }; + key { [ Right, NoSymbol, rightarrow, 0x10021D2 ] }; +}; diff --git a/home.nix b/home.nix new file mode 100644 index 0000000..ab5c918 --- /dev/null +++ b/home.nix @@ -0,0 +1,337 @@ +inputs: { config, lib, pkgs, osConfig, ... }: +let + inherit (osConfig) hardware; + + graphical = + if lib.versionAtLeast osConfig.system.stateVersion "24.11" + then hardware.graphic.enable + else hardware.opengl.enable; +in { + imports = [ + inputs.nur.hmModules.nur + inputs.en_EU.homeModules.default + inputs.nix-index-database.hmModules.nix-index + inputs.stylix.homeManagerModules.stylix + + ./firefox.nix + ./wayland.nix + ]; + + home.stateVersion = "24.11"; + home.enableNixpkgsReleaseCheck = false; + + home.activation = { + fish = lib.hm.dag.entryAfter ["writeBoundary"] '' + run ${lib.getExe config.programs.fish.package} -c 'set -U fish_greeting' + ''; + }; + + home.packages = with pkgs; [ + # Terminfo + kitty.terminfo + + # Core utilities + (lib.meta.setPrio 0 uutils-coreutils-noprefix) + + # Text manipulation + delta + sd + skim + + # Networking + dogdns + whois + xh + + # Filesystem + file + #xcp + + # Development + pijul + + # Calculator + fend + ]; + + editorconfig = { + enable = true; + settings = { + "*" = { + indent_style = "tab"; + tab_width = 4; + end_of_line = "lf"; + charset = "utf-8"; + trim_trailing_whitespace = true; + insert_final_newline = true; + }; + + "*.nix" = { + indent_style = "space"; + indent_size = 2; + }; + }; + }; + + home.shellAliases = { + cat = "${pkgs.libarchive}/bin/bsdcat"; + icat = "kitten icat"; + }; + + home.preferXdgDirectories = true; + + programs.aria2 = { + enable = true; + settings = { + max-concurrent-downloads = 4; + max-connection-per-server = 2; + min-split-size = "16M"; + remote-time = true; + split = 4; + http-accept-gzip = true; + max-overall-upload-limit = "256K"; + dscp = 8; + enable-mmap = true; + file-allocation = "falloc"; + }; + }; + + programs.bat.enable = true; + + programs.bottom = { + enable = true; + settings.flags = { + group = true; + battery = true; + color = "gruvbox"; + mem_as_value = true; + network_use_binary_prefix = true; + network_use_bytes = true; + }; + }; + + programs.eza = { + enable = true; + icons = true; + git = true; + + extraOptions = [ + "--binary" + "--colour=automatic" + "--colour-scale=all" + "--colour-scale-mode=gradient" + "--group-directories-first" + ]; + }; + + programs.fd.enable = true; + + programs.fish = { + enable = true; + functions = { + fish_prompt = '' + set -l user_colour 'green' + if fish_is_root_user + set user_colour 'red' + end + + echo -n -s (set_color $user_colour --bold) $USER@ (prompt_hostname) \ + (set_color blue --bold) ' ' (prompt_pwd) ' ❯ ' (set_color normal) + ''; + + fish_right_prompt = '' + set -l st $status + + if test $st -ne 0 + set_color red --bold + printf "%s " (sysexit $st) + set_color normal + end + ''; + + fish_title = "prompt_pwd"; + + sysexit = builtins.readFile ./sysexit.fish; + }; + + interactiveShellInit = '' + if type -q tabs + tabs -4 + end + ''; + }; + + programs.git = { + enable = true; + delta.enable = true; + + userName = "Mikael Voss"; + userEmail = "mvs@nyantec.com"; + + extraConfig = { + core = { + eol = "lf"; + fsync = "committed"; + }; + + init.defaultBranch = "main"; + pull.rebase = true; + push.autoSetupRemote = true; + rebase.autoStash = true; + }; + }; + + programs.gpg = { + enable = hardware.gpgSmartcards.enable; + scdaemonSettings.disable-ccid = true; + }; + + programs.helix = { + enable = true; + defaultEditor = true; + settings = { + editor.auto-pairs = { + "“" = "”"; + "‘" = "’"; + "„" = "“"; + "‚" = "‘"; + }; + + editor.whitespace.render = { + nbsp = "all"; + nnbsp = "all"; + tab = "all"; + }; + + editor.whitespace.characters = { + nbsp = "␣"; + nnbsp = "⍽"; + tab = "»"; + tabpad = "·"; + }; + + keys.normal = { + minus = "command_mode"; + r = "move_char_left"; + n = "move_visual_line_down"; + t = "move_visual_line_up"; + h = "move_char_right"; + }; + }; + }; + + programs.jq.enable = true; + programs.man.generateCaches = osConfig.documentation.man.generateCaches; + programs.ripgrep.enable = true; + + programs.ssh = { + enable = true; + compression = true; + matchBlocks = { + "*.nyantec.com".user = "mvs"; + "solitary.social" = { + user = "nil"; + forwardAgent = true; + }; + }; + + serverAliveInterval = 10; + serverAliveCountMax = 60; + }; + + programs.vim = { + enable = true; + settings = { + background = "dark"; + expandtab = false; + number = true; + shiftwidth = 4; + tabstop = 4; + }; + extraConfig = '' + " no Vi compatibility + set nocompatible + + " Unicode support + set encoding=utf-8 + + " special characters + set list + set listchars=tab:»·,trail:·,extends:… + + set ruler + + " movement + noremap r h + noremap R H + noremap n j + noremap t k + noremap h l + noremap H L + + " beginning of previous word + noremap p b + + " end of word + noremap l e + noremap L E + + " change one char + noremap X s + + " repeat search + noremap ; n + noremap : N + + " paste + noremap s p + noremap S P + + " join lines + noremap N J + + " change + noremap e c + noremap E C + + " replace + noremap z r + noremap Z R + + " inclusive jump + noremap m f + noremap M F + + " exlusive jump + noremap f t + noremap F T + + " command mode + noremap - : + ''; + }; + + services.gpg-agent = { + enable = hardware.gpgSmartcards.enable; + enableSshSupport = true; + pinentryPackage = + if graphical + then pkgs.pinentry-gtk2 + else pkgs.pinentry-curses; + }; + + xdg.userDirs = + let + home = config.home.homeDirectory; + in { + enable = true; + desktop = "${home}/tmp"; + documents = "${home}/var"; + download = "${home}/tmp"; + pictures = "${home}/img"; + music = "${home}/msc"; + publicShare = null; + templates = null; + videos = null; + }; +} diff --git a/module.nix b/module.nix new file mode 100644 index 0000000..57d05c5 --- /dev/null +++ b/module.nix @@ -0,0 +1,30 @@ +inputs: user: { config, lib, pkgs, ... }: +let + inherit (config) hardware; + + graphical = + if lib.versionAtLeast config.system.stateVersion "24.11" + then hardware.graphic.enable + else hardware.opengl.enable; +in { + imports = [ inputs.home-manager.nixosModules.home-manager ]; + + environment.etc."xkb/symbols/greedy" = lib.mkIf graphical + { source = ./greedy.xkb; }; + + home-manager = { + useUserPackages = lib.mkDefault true; + useGlobalPkgs = lib.mkDefault true; + users.${user} = inputs.self.homeConfigurations.default; + }; + + programs.fish.enable = true; + + users.users.${user} = { + isNormalUser = true; + shell = config.programs.fish.package; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGOTvXiNHTXq9wkcxdVOblHVyvcAaCfxmJp/CXI4rzMj" + ]; + }; +} diff --git a/sysexit.fish b/sysexit.fish new file mode 100644 index 0000000..03f94ac --- /dev/null +++ b/sysexit.fish @@ -0,0 +1,104 @@ +if status is-interactive + function sysexit + switch $argv[1] + case 0 + echo OK + case 64 + echo USAGE + case 65 + echo DATAERR + case 66 + echo NOINPUT + case 67 + echo NOUSER + case 68 + echo NOHOST + case 69 + echo UNAVAILABLE + case 70 + echo SOFTWARE + case 71 + echo OSERR + case 72 + echo OSFILE + case 73 + echo CANTCREAT + case 74 + echo IOERR + case 75 + echo TEMPFAIL + case 76 + echo PROTOCOL + case 77 + echo NOPERM + case 78 + echo CONFIG + case 127 + echo NOTFOUND + case 129 + echo SIGHUP + case 130 + echo SIGINT + case 131 + echo SIGQUIT + case 132 + echo SIGILL + case 133 + echo SIGTRAP + case 134 + echo SIGABRT + case 135 + echo SIGBUS + case 136 + echo SIGFPE + case 137 + echo SIGKILL + case 138 + echo SIGUSR1 + case 139 + echo SIGSEGV + case 140 + echo SIGUSR2 + case 141 + echo SIGPIPE + case 142 + echo SIGALRM + case 143 + echo SIGTERM + case 144 + echo SIGSTKFLT + case 145 + echo SIGCHLD + case 146 + echo SIGCONT + case 147 + echo SIGSTOP + case 148 + echo SIGTSTP + case 149 + echo SIGTTIN + case 150 + echo SIGTTOU + case 151 + echo SIGURG + case 152 + echo SIGXCPU + case 153 + echo SIGXFSZ + case 154 + echo SIGVTALRM + case 155 + echo SIGPROF + case 156 + echo SIGWINCH + case 157 + echo SIGIO + case 158 + echo SIGPWR + case 159 + echo SIGSYS + case '*' + echo $argv[1] + end + end +end diff --git a/wallpaper.png b/wallpaper.png new file mode 100644 index 0000000..a0949c5 Binary files /dev/null and b/wallpaper.png differ diff --git a/wayland.nix b/wayland.nix new file mode 100644 index 0000000..e47a2ba --- /dev/null +++ b/wayland.nix @@ -0,0 +1,652 @@ +{ config, lib, pkgs, osConfig, ... }: +let + inherit (osConfig) hardware; + + graphical = + if lib.versionAtLeast osConfig.system.stateVersion "24.11" + then hardware.graphic.enable + else hardware.opengl.enable; + + cmd = { + brightnessctl = "${pkgs.brightnessctl}/bin/brightnessctl"; + fish = "${osConfig.programs.fish.package}/bin/fish"; + grim = "${pkgs.grim}/bin/grim"; + hyprctl = "${config.wayland.windowManager.hyprland.finalPackage}/bin/hyprctl"; + hyprlock = "${config.programs.hyprlock.package}/bin/hyprlock"; + jq = "${config.programs.jq.package}/bin/jq"; + keepassxc = "${pkgs.keepassxc}/bin/keepassxc"; + kitty = ''${config.programs.kitty.package}/bin/kitty --single-instance --instance-group "$XDG_SESSION_ID"''; + loginctl = "${osConfig.systemd.package}/bin/loginctl"; + mpv = "${config.programs.mpv.package}/bin/mpv"; + pidof = "${pkgs.procps}/bin/pidof"; + playerctl = "${pkgs.playerctl}/bin/playerctl"; + slurp = "${pkgs.slurp}/bin/slurp"; + tofi-run = "${config.programs.tofi.package}/bin/tofi-run"; + wl-copy = "${pkgs.wl-clipboard}/bin/wl-copy"; + wpctl = "${osConfig.services.pipewire.wireplumber.package}/bin/wpctl"; + xdg-open = "${pkgs.xdg-utils}/bin/xdg-open"; + }; +in lib.mkIf graphical { + home.packages = with pkgs; [ + # Image processing + oxipng + + # Documentation + linux-manual + man-pages + man-pages-posix + + # System operations + restic + + # Cryptography + age + + # Messaging + element-desktop + #signal-desktop + + # Audio control + pwvucontrol + + evince + #inkscape + obsidian + + kicad + calibre + #enpass + keepassxc + + # fonts + fira-code + font-awesome + lato + + # Multimedia + jellyfin-mpv-shim + + libreoffice + ]; + + fonts.fontconfig.enable = true; + + home.keyboard = { + layout = "greedy"; + options = "ctrl:nocaps"; + }; + + programs.beets = { + enable = true; + settings = { + directory = "~/msc"; + import.reflink = "auto"; + + plugins = [ + "chroma" + "spotify" + "fromfilename" + + "fetchart" + "lyrics" + "replaygain" + + "duplicates" + "hook" + ]; + + hook.hooks = [ + { + event = "import"; + command = "systemctl --user start mopidy-scan.service"; + } + ]; + }; + }; + + programs.hyprlock = { + enable = true; + settings = { + general = { + hide_cursor = true; + }; + + background = { + path = "screenshot"; + blur_passes = 3; + contrast = 1.25; + }; + }; + }; + + programs.imv.enable = true; + + programs.kitty = + let + font-features = "+ss01 +ss06 +zero +onum"; + in { + enable = true; + theme = "Catppuccin-Mocha"; + settings = { + disable_ligatures = "cursor"; + "font_features FiraCodeRoman_400wght" = font-features; + "font_features FiraCodeRoman_500wght" = font-features; + "font_features FiraCodeRoman_600wght" = font-features; + "font_features FiraCodeRoman_700wght" = font-features; + + cursor_blink_interval = 0; + + scrollback_lines = 65536; + + enable_audio_bell = false; + + close_on_child_death = true; + + clear_all_shortcuts = true; + }; + + keybindings = { + "ctrl+shift+c" = "copy_to_clipboard"; + "ctrl+shift+v" = "paste_from_clipboard"; + "ctrl+shift+s" = "paste_from_selection"; + "shift+insert" = "paste_from_selection"; + "ctrl+up" = "scroll_line_up"; + "ctrl+down" = "scroll_line_down"; + "ctrl+page_up" = "scroll_page_up"; + "ctrl+page_down" = "scroll_page_down"; + "shift+page_up" = "scroll_page_up"; + "shift+page_down" = "scroll_page_down"; + "ctrl+home" = "scroll_home"; + "ctrl+end" = "scroll_end"; + "ctrl+print_screen" = "show_scrollback"; + + "ctrl+equal" = "change_font_size all 0"; + "ctrl+plus" = "change_font_size all +1"; + "ctrl+minus" = "change_font_size all -1"; + + "ctrl+shift+u" = "kitten unicode_input"; + }; + }; + + programs.mpv = { + enable = true; + defaultProfiles = [ "high-quality" ]; + config = { + #access-references = false; + + # video output + vo = "gpu"; + #gpu-api = "vulkan"; + hwdec = "vulkan,vaapi,auto-safe"; + vd-lavc-dr = true; + + scale = "ewa_lanczos4sharpest"; + cscale = "spline64"; + dscale = "mitchell"; + tscale = "oversample"; + + # A/V sync + video-sync = "display-resample"; + interpolation = true; + + # audio + volume = 100; + volume-max = 100; + + # subtitles + sub-auto = "fuzzy"; + + # screenshots + screenshot-format = "avif"; + + # cache + demuxer-max-bytes = "768MiB"; + demuxer-max-back-bytes = "256MiB"; + }; + + profiles = { + highres = { + scale = "spline64"; + }; + }; + + scripts = with pkgs.mpvScripts; [ + mpris + #autocrop + autodeint + ]; + }; + + programs.texlive = { + enable = true; + extraPackages = tpkgs: { + inherit (tpkgs) + texlive-scripts + + xelatex-dev + fontspec + polyglossia + + hyphen-english + hyphen-french + hyphen-german + hyphen-portuguese + hyphen-spanish + + koma-script + + amsmath + csquotes + hyperref + paralist + realscripts + unicode-math + units + xecjk + xecolor + xltxtra + ; + }; + }; + + programs.thunderbird = { + enable = true; + package = pkgs.thunderbird; + profiles = { }; + }; + + programs.waybar = { + enable = true; + systemd = { + enable = true; + target = "hyprland-session.target"; + }; + + settings = { + mainBar = { + layer = "top"; + position = "bottom"; + spacing = 4; + modules-left = [ "hyprland/workspaces" ]; + modules-center = [ "hyprland/window" ]; + modules-right = [ "tray" "network" "pulseaudio" "backlight" "battery" "temperature" "cpu" "memory" "clock" ]; + + "hyprland/workspaces" = { + #format = "{icon}"; + #format-icons.urgent = ""; + }; + + "hyprland/window".max-length = 64; + temperature = { + critical-threshold = 80; + format = "{icon} {temperatureC} °C"; + format-icons = [ "" "" "" ]; + }; + + cpu.format = " {} %"; + memory.format = " {} %"; + battery = { + format = "{icon} {capacity} %"; + format-icons = [ "" "" "" "" "" ]; + }; + + network.format = " {essid} ({signalStrength} %)"; + + pulseaudio = { + format = "{icon} {volume} %"; + format-muted = ""; + format-icons = { + headphone = ""; + hands-free = ""; + headset = ""; + phone = ""; + portable = ""; + car = ""; + default = [ "" "" "" ]; + }; + }; + + backlight = { + format = "{icon} {percent} %"; + format-icons = [ "" "" ]; + }; + + clock = { + format = " {:%H:%M %Z}"; + format-alt = " {:%Y-%m-%d}"; + }; + }; + }; + }; + + programs.yt-dlp.enable = true; + + services.gammastep = { + enable = true; + provider = "geoclue2"; + settings = { + general.adjustment-method = "wayland"; + }; + }; + + services.hypridle = { + enable = true; + settings = { + general = with cmd; { + lock_cmd = "${pidof} hyprlock || ${hyprlock}"; + before_sleep_cmd = "${loginctl} lock-session"; + after_sleep_cmd = "${hyprctl} dispatch dpms on"; + }; + + listener = with cmd; [ + { + timeout = 210; + on-timeout = "${brightnessctl} --save -e set 20%-"; + on-resume = "${brightnessctl} --save -e set +20%"; + } { + timeout = 240; + on-timeout = "${loginctl} lock-session"; + } { + timeout = 270; + on-timeout = "${hyprctl} dispatch dpms off"; + on-resume = "${hyprctl} dispatch dpms on"; + } + ]; + }; + }; + + services.hyprpaper = + let + wallpaper = toString ./wallpaper.png; + in { + enable = true; + settings = { + ipc = false; + preload = [ wallpaper ]; + wallpaper = [ wallpaper ]; + }; + }; + + services.mako = { + enable = true; + defaultTimeout = 5000; + }; + + services.mopidy = { + enable = true; + extensionPackages = with pkgs; [ + mopidy-iris + mopidy-local + mopidy-mpd + mopidy-mpris + ]; + settings = { + core = { + cache_dir = "$XDG_CACHE_DIR/mopidy"; + config_dir = "$XDG_CONFIG_DIR/mopidy"; + data_dir = "$XDG_DATA_DIR/mopidy"; + }; + + audio.mixer = "none"; + file.media_dirs = [ "$XDG_MUSIC_DIR" ]; + local.media_dir = "$XDG_MUSIC_DIR"; + + mpd.hostname = "localhost"; + + http = { + hostname = "localhost"; + port = 6680; + default_app = "iris"; + }; + }; + }; + + services.pasystray.enable = true; + + services.syncthing = { + enable = true; + tray.enable = true; + }; + + services.udiskie = { + enable = true; + automount = false; + }; + + stylix = { + enable = true; + + image = ./wallpaper.png; + base16Scheme = "${pkgs.base16-schemes}/share/themes/catppuccin-macchiato.yaml"; + + fonts = { + sansSerif = { + package = pkgs.lato; + name = "Lato"; + }; + + monospace = { + package = pkgs.fira-code; + name = "Fira Code"; + }; + + sizes.terminal = 11; + }; + + targets = { + hyprpaper.enable = lib.mkForce false; + }; + }; + + systemd.user.targets.tray = { + Unit = { + BindsTo = "waybar.service"; + After = "waybar.service"; + }; + }; + + wayland.windowManager.hyprland = { + enable = true; + + settings = { + monitor = [ + #"desc:LG Display 0x06AA, 1440x900, 0x0, 1, bitdepth, 10" + "desc:LG Display 0x06AA, highres, 0x2160, 1, bitdepth, 10" + "desc:Lenovo Group Limited P40w-20 V9084N0R, highres, 0x0, 1, bitdepth, 10" + ]; + + input = { + kb_layout = "greedy"; + kb_options = "ctrl:nocaps"; + }; + + general = { + gaps_in = 4; + gaps_out = 8; + + layout = "dwindle"; + }; + + gestures.workspace_swipe = true; + + misc = { + disable_hyprland_logo = true; + vrr = 1; + no_direct_scanout = false; + background_color = lib.mkForce "0x181825"; + }; + + decoration = { + rounding = 8; + }; + + dwindle.preserve_split = true; + + workspace = [ + "name:0, monitor:desc:LG Display 0x06AA, default:true" + "name:A, monitor:desc:LG Display 0x06AA" + ]; + + "$mod" = "SUPER"; + + bind = with cmd; [ + # exit + "$mod SHIFT, code:28, exit" + + # window state + "$mod SHIFT, Space, togglefloating" + "$mod, code:32, fullscreen" + "$mod, code:48, togglesplit" + "$mod SHIFT, code:24, killactive" + + # change focus + "$mod, left, movefocus, l" + "$mod, code:43, movefocus, l" + "$mod, right, movefocus, r" + "$mod, code:46, movefocus, r" + "$mod, up, movefocus, u" + "$mod, code:45, movefocus, u" + "$mod, down, movefocus, d" + "$mod, code:44, movefocus, d" + + # move window + "$mod SHIFT, left, movewindow, l" + "$mod SHIFT, code:43, movewindow, l" + "$mod SHIFT, right, movewindow, r" + "$mod SHIFT, code:46, movewindow, r" + "$mod SHIFT, up, movewindow, u" + "$mod SHIFT, code:45, movewindow, u" + "$mod SHIFT, down, movewindow, d" + "$mod SHIFT, code:44, movewindow, d" + + # resize window + "$mod CTRL, left, resizeactive, -20 0" + "$mod CTRL, code:43, resizeactive, -20 0" + "$mod CTRL, right, resizeactive, 20 0" + "$mod CTRL, code:46, resizeactive, 20 0" + "$mod CTRL, up, resizeactive, 0 -20" + "$mod CTRL, code:45, resizeactive, 0 -20" + "$mod CTRL, down, resizeactive, 0 20" + "$mod CTRL, code:44, resizeactive, 0 20" + + # move floating + "$mod ALT, left, moveactive, -20 0" + "$mod ALT, code:43, moveactive, -20 0" + "$mod ALT, right, moveactive, 20 0" + "$mod ALT, code:46, moveactive, 20 0" + "$mod ALT, up, moveactive, 0 -20" + "$mod ALT, code:45, moveactive, 0 -20" + "$mod ALT, down, moveactive, 0 20" + "$mod ALT, code:44, moveactive, 0 20" + + # switch workspaces + "$mod, code:49, workspace, name:0" + "$mod, code:10, workspace, name:1" + "$mod, code:11, workspace, name:2" + "$mod, code:12, workspace, name:3" + "$mod, code:13, workspace, name:4" + "$mod, code:14, workspace, name:5" + "$mod, code:15, workspace, name:6" + "$mod, code:16, workspace, name:7" + "$mod, code:17, workspace, name:8" + "$mod, code:18, workspace, name:9" + "$mod, code:19, workspace, name:A" + "$mod, code:20, togglespecialworkspace" + + "$mod, mouse_down, workspace, e+1" + "$mod, mouse_up, workspace, e-1" + + # send to workspaces + "$mod SHIFT, code:49, movetoworkspacesilent, name:0" + "$mod SHIFT, code:10, movetoworkspacesilent, name:1" + "$mod SHIFT, code:11, movetoworkspacesilent, name:2" + "$mod SHIFT, code:12, movetoworkspacesilent, name:3" + "$mod SHIFT, code:13, movetoworkspacesilent, name:4" + "$mod SHIFT, code:14, movetoworkspacesilent, name:5" + "$mod SHIFT, code:15, movetoworkspacesilent, name:6" + "$mod SHIFT, code:16, movetoworkspacesilent, name:7" + "$mod SHIFT, code:17, movetoworkspacesilent, name:8" + "$mod SHIFT, code:18, movetoworkspacesilent, name:9" + "$mod SHIFT, code:19, movetoworkspacesilent, name:A" + "$mod SHIFT, code:20, movetoworkspacesilent, special" + + # function keys + ", XF86MonBrightnessUp, exec, ${brightnessctl} -e set +5%" + ", XF86MonBrightnessDown, exec, ${brightnessctl} -e set 5%-" + ", XF86AudioRaiseVolume, exec, ${wpctl} set-volume @DEFAULT_AUDIO_SINK@ +2dB" + ", XF86AudioLowerVolume, exec, ${wpctl} set-volume @DEFAULT_AUDIO_SINK@ -2dB" + ", XF86AudioMute, exec, ${wpctl} set-mute @DEFAULT_AUDIO_SINK@ toggle" + ", XF86AudioMicMute, exec, ${wpctl} set-mute @DEFAULT_AUDIO_SOURCE@ toggle" + ", XF86AudioNext, exec, ${playerctl} next" + ", XF86AudioPrev, exec, ${playerctl} previous" + ", XF86AudioPlay, exec, ${playerctl} play" + ", XF86AudioStop, exec, ${playerctl} pause" + ", XF86Explorer, exec, ${xdg-open} https:" + + # screenshots + "$mod, Print, exec, ${grim} -l 9 - | ${wl-copy}" + "$mod SHIFT, Print, exec, ${slurp} | ${grim} -g - -l 9 - | ${wl-copy}" + ''$mod CTRL, Print, exec, ${grim} -g "$(${hyprctl} -j activewindow ${jq} -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')" -l 9 - | ${wl-copy}'' + "$mod, Return, exec, ${kitty}" + "$mod Shift, Return, exec, ${kitty} ${fish} --private" + "$mod, code:55, exec, $(${tofi-run})" + "$mod, code:56, exec, ${loginctl} lock-session" + ]; + + bindm = [ + # move window + "$mod, mouse:272, movewindow" + + # resize window + "$mod, mouse:273, resizewindow" + ]; + + "$keepassPopup" = ''class:^org\.keepassxc\.KeePassXC$, title:^(Unlock.*|.*Access Request)$''; + + windowrulev2 = [ + ''workspace name:A silent, class:^org\.keepassxc\.KeePassXC$, title:^Vault\.kdbx'' + "float, $keepassPopup" + "center, $keepassPopup" + "dimaround, $keepassPopup" + "stayfocused, $keepassPopup" + ]; + + exec-once = with cmd; [ + "${keepassxc}" + ]; + + env = [ + "__GL_GSYNC_ALLOWED, 1" + "__GL_SYNC_TO_VBLANK, 1" + "__GL_VRR_ALLOWED, 1" + "CLUTTER_BACKEND, wayland" + "GDK_BACKEND, wayland,x11" + "QT_QPA_PLATFORM, wayland;xcb" + #"SDL_VIDEODRIVER, wayland" + "WLR_NO_HARDWARE_CURSORS, 1" + ]; + + animations = { + bezier = [ + "wind, 0.2, 0.9, 0.2, 1.05" + "winMov, 0.2, 0.9, 0.2, 1.08" + "winIn, 0.2, 0.9, 0.2, 1.08" + "winOut, 0.2, 0, 0.9, 0.2" + "liner, 1, 1, 1, 1" + ]; + + animation = [ + "windows, 1, 4, wind, slide" + "windowsIn, 1, 4, winIn, slide" + "windowsOut, 1, 4, winOut, slide" + "windowsMove, 1, 4, winMov, slide" + "fade, 1, 4, default" + "fadeOut, 1, 4, default" + "workspaces, 1, 4, wind" + ]; + }; + + cursor.no_hardware_cursors = true; + }; + }; +}