From 2c3b029c83aa0f8f3b3251cdd1f7243d511891e1 Mon Sep 17 00:00:00 2001 From: mads Date: Wed, 1 Jul 2026 18:05:21 +0000 Subject: [PATCH] NixOS flake: mbp (Asahi) + pc (x86), Hyprland/Zen/neovim, README --- .gitignore | 3 + README.md | 161 +++++++++++++++++++++ config/eww/eww.scss | 14 ++ config/eww/eww.yuck | 17 +++ config/eww/scripts/battery-capacity | 8 ++ config/eww/scripts/battery-status | 7 + config/fuzzel/fuzzel.ini | 19 +++ config/hypr/hypridle.conf | 21 +++ config/hypr/hyprland.conf | 146 +++++++++++++++++++ config/hypr/hyprlock.conf | 34 +++++ config/nvim/init.lua | 114 +++++++++++++++ config/nvim/nvim-pack-lock.json | 68 +++++++++ firmware/README.md | 25 ++++ flake.lock | 132 +++++++++++++++++ flake.nix | 208 +++++++++++++++++++++++++++ hosts/mbp/hardware-configuration.nix | 14 ++ hosts/pc/hardware-configuration.nix | 14 ++ 17 files changed, 1005 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 config/eww/eww.scss create mode 100644 config/eww/eww.yuck create mode 100755 config/eww/scripts/battery-capacity create mode 100755 config/eww/scripts/battery-status create mode 100644 config/fuzzel/fuzzel.ini create mode 100644 config/hypr/hypridle.conf create mode 100644 config/hypr/hyprland.conf create mode 100644 config/hypr/hyprlock.conf create mode 100644 config/nvim/init.lua create mode 100644 config/nvim/nvim-pack-lock.json create mode 100644 firmware/README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 hosts/mbp/hardware-configuration.nix create mode 100644 hosts/pc/hardware-configuration.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faea756 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# nix build outputs +result +result-* diff --git a/README.md b/README.md new file mode 100644 index 0000000..f5f9071 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# mads — NixOS config (one flake, two machines) + +Declarative NixOS for: + +| host | `nixosConfigurations.` | arch | notes | +|------|------------------------------|------|-------| +| MacBook (M1 Pro, Asahi) | `mbp` | `aarch64-linux` | Apple Silicon support module + vendored firmware | +| Arbitrary x86 PC | `pc` | `x86_64-linux` | plain NixOS | + +Everything shared (Hyprland, greetd autologin, Zen+Tridactyl, neovim, zsh+starship, +packages, user) lives in the `common` / `home` modules in `flake.nix`. Host-specific +bits (Asahi firmware/cache vs. x86 boot) are the small inline modules per host. + +``` +flake.nix inputs + common/home modules + both hosts +hosts/mbp/hardware-configuration.nix ← DUMMY, regenerate on the Mac +hosts/pc/hardware-configuration.nix ← DUMMY, regenerate on the PC +config/ hypr eww fuzzel nvim raw configs (sourced by home-manager) +firmware/ Wi-Fi/BT blobs (Mac only, git-add at install) +``` + +> **Keep this repo private** — the Mac firmware under `firmware/` is non-redistributable. + +The `hardware-configuration.nix` files are placeholders so the flake evaluates +off-target; each is overwritten by `nixos-generate-config` during that machine's install. + +--- + +## Rename a host / set your timezone + +- Host name: change the `nixosConfigurations.` key **and** its + `networking.hostName` in `flake.nix` (e.g. `pc` → `desktop`). +- `time.timeZone` is in the `common` module. + +--- + +## Install on the MacBook (Asahi + NixOS) + +Follows the official guide — **always check the latest**, as steps change: + +(this config tracks Asahi `main`; currently kernel `linux-asahi 7.0.13`). + +> ⚠ **Back up first.** Damaging the GPT / first / last partition can brick the Mac +> and lose all data. + +### 1. macOS → install the UEFI environment +In macOS Terminal (admin account): +```sh +curl https://alx.sh | sh +``` +- `r` — resize macOS, leave ≥ 20 GB free for NixOS +- `f` — install into free space → **UEFI environment only**, name it `NixOS` +- Reboot into recovery when told; set **permissive security** for the new OS. +- It reboots into U-Boot (Asahi + U-Boot logos). Power off. + +### 2. Build + write the installer USB +On any Linux box with Nix (or download a release ISO from the repo's Releases): +```sh +nix build github:nix-community/nixos-apple-silicon#installer-bootstrap -o installer +sudo dd if=installer/iso/nixos-*.iso of=/dev/sdX bs=4M conv=fsync status=progress +``` +(`dd` only — not unetbootin.) + +### 3. Boot the installer, partition, mount +Boot the USB (U-Boot → GRUB → installer), then: +```sh +sudo su +sgdisk /dev/nvme0n1 -n 0:0 -s # root fills free space +sgdisk /dev/nvme0n1 -p # find the new 8300 partition number (e.g. p5) +mkfs.ext4 -L nixos /dev/nvme0n1p5 + +mount /dev/disk/by-label/nixos /mnt +mkdir -p /mnt/boot +mount /dev/disk/by-partuuid/$(cat /proc/device-tree/chosen/asahi,efi-system-partition) /mnt/boot +``` + +### 4. Hardware config + vendored firmware + this flake +```sh +nixos-generate-config --root /mnt + +# get this repo onto the target (private remote or USB), e.g.: +git clone /mnt/etc/nixos/config && cd /mnt/etc/nixos/config + +cp /mnt/etc/nixos/hardware-configuration.nix hosts/mbp/hardware-configuration.nix + +# vendor the Wi-Fi/BT firmware (pure flakes can't read /boot/asahi) +mkdir -p firmware +cp /mnt/boot/asahi/all_firmware.tar.gz firmware/ +cp /mnt/boot/asahi/kernelcache* firmware/ +git add -f hosts/mbp/hardware-configuration.nix firmware/* +``` + +### 5. Install +```sh +nixos-install --flake .#mbp # sets root password at the end +reboot +``` +Boot picker: hold the power button. greetd autologins `mads` into Hyprland. +**First thing after login: `passwd`** (the config ships `initialPassword = "changeme"`). + +--- + +## Install on an x86 PC + +Standard NixOS install (), +just point `nixos-install` at `.#pc`. + +### 1. Boot the NixOS ISO, get network, partition (UEFI/GPT) +```sh +sudo -i +parted /dev/nvme0n1 -- mklabel gpt +parted /dev/nvme0n1 -- mkpart ESP fat32 1MiB 512MiB +parted /dev/nvme0n1 -- set 1 esp on +parted /dev/nvme0n1 -- mkpart primary 512MiB 100% + +mkfs.fat -F32 -n BOOT /dev/nvme0n1p1 +mkfs.ext4 -L nixos /dev/nvme0n1p2 + +mount /dev/disk/by-label/nixos /mnt +mkdir -p /mnt/boot +mount /dev/disk/by-label/BOOT /mnt/boot +``` +(The `BOOT` / `nixos` labels match the dummy `hosts/pc/hardware-configuration.nix`, +but the generated one below replaces it with real UUIDs anyway.) + +### 2. Hardware config + this flake + install +```sh +nixos-generate-config --root /mnt +git clone /mnt/etc/nixos/config && cd /mnt/etc/nixos/config +cp /mnt/etc/nixos/hardware-configuration.nix hosts/pc/hardware-configuration.nix + +nixos-install --flake .#pc +reboot +``` +Then `passwd`. + +--- + +## Day-to-day + +```sh +# apply config changes +sudo nixos-rebuild switch --flake ~/nixos#mbp # or #pc + +# update everything (nixpkgs + asahi + zen + addons), then rebuild +nix flake update +sudo nixos-rebuild switch --flake ~/nixos#mbp + +# roll back: pick a previous generation in the boot menu +``` + +Both `apple-silicon` and `nixpkgs` are cached (asahi via its Cachix), so updates +download rather than compile. + +--- + +## Verify-on-first-boot (can't be checked off-machine) + +- **greetd session** — the command is `uwsm start hyprland`. If you land on a blank + tty instead of Hyprland, change it to `command = "Hyprland"` in `common`. +- **Asahi `hardware.asahi.*`** — firmware path / sound options, per the guide above. diff --git a/config/eww/eww.scss b/config/eww/eww.scss new file mode 100644 index 0000000..3041c68 --- /dev/null +++ b/config/eww/eww.scss @@ -0,0 +1,14 @@ +* { + all: unset; +} + +.battery-popup { + background-color: #16181a; + padding: 6px 10px; +} + +.battery-popup-text { + font-family: monospace; + font-size: 14px; + color: #ddd; +} diff --git a/config/eww/eww.yuck b/config/eww/eww.yuck new file mode 100644 index 0000000..280cab1 --- /dev/null +++ b/config/eww/eww.yuck @@ -0,0 +1,17 @@ +(defpoll battery-percent :interval "10s" "scripts/battery-capacity") +(defpoll battery-status :interval "10s" "scripts/battery-status") + +(defwidget battery-popup [] + (box :class "battery-popup" + :halign "center" + :valign "center" + (label :class "battery-popup-text" + :text "${battery-percent}% ${battery-status}"))) + +(defwindow battery-popup + :monitor 0 + :geometry (geometry :anchor "center") + :stacking "overlay" + :exclusive false + :focusable false + (battery-popup)) diff --git a/config/eww/scripts/battery-capacity b/config/eww/scripts/battery-capacity new file mode 100755 index 0000000..4c333f3 --- /dev/null +++ b/config/eww/scripts/battery-capacity @@ -0,0 +1,8 @@ +#!/bin/sh +# Print the charge % of the first real battery. Portable across machines +# (macsmc-battery on Asahi, BAT0/BAT1 on x86). +for d in /sys/class/power_supply/*; do + [ "$(cat "$d/type" 2>/dev/null)" = "Battery" ] || continue + cat "$d/capacity" 2>/dev/null && exit 0 +done +echo "?" diff --git a/config/eww/scripts/battery-status b/config/eww/scripts/battery-status new file mode 100755 index 0000000..68d7448 --- /dev/null +++ b/config/eww/scripts/battery-status @@ -0,0 +1,7 @@ +#!/bin/sh +# Print the charging status of the first real battery. Portable across machines. +for d in /sys/class/power_supply/*; do + [ "$(cat "$d/type" 2>/dev/null)" = "Battery" ] || continue + cat "$d/status" 2>/dev/null && exit 0 +done +echo "Unknown" diff --git a/config/fuzzel/fuzzel.ini b/config/fuzzel/fuzzel.ini new file mode 100644 index 0000000..87f0536 --- /dev/null +++ b/config/fuzzel/fuzzel.ini @@ -0,0 +1,19 @@ +[main] +font=JetBrainsMono Nerd Font:size=12 +terminal=kitty +lines=8 +width=40 +horizontal-pad=16 +vertical-pad=12 +inner-pad=4 + +[colors] +background=16181aff +text=ffffffff +match=ff6e5eff +selection=3c4048ff +selection-text=ffffffff + +[border] +width=0 +radius=0 diff --git a/config/hypr/hypridle.conf b/config/hypr/hypridle.conf new file mode 100644 index 0000000..cc80713 --- /dev/null +++ b/config/hypr/hypridle.conf @@ -0,0 +1,21 @@ +general { + lock_cmd = pidof hyprlock || hyprlock + before_sleep_cmd = loginctl lock-session + after_sleep_cmd = hyprctl dispatch dpms on +} + +listener { + timeout = 300 + on-timeout = brightnessctl -s set 10 + on-resume = brightnessctl -r +} + +listener { + timeout = 600 + on-timeout = loginctl lock-session +} + +listener { + timeout = 1800 + on-timeout = systemctl suspend +} diff --git a/config/hypr/hyprland.conf b/config/hypr/hyprland.conf new file mode 100644 index 0000000..c98ee51 --- /dev/null +++ b/config/hypr/hyprland.conf @@ -0,0 +1,146 @@ +# -- Monitors & input defaults (overridable per-host) -- +monitor = , preferred, auto, 2.0 + +device { + name = apple-spi-keyboard + kb_options = caps:ctrl_modifier,grp:win_space_toggle +} + +# TrackPoint (Corne nipple): hold middle button + move to scroll, joystick-style +device { + name = corne-tp + scroll_method = on_button_down + scroll_button = 274 # BTN_MIDDLE + scroll_button_lock = false # set true to toggle scroll on/off with a tap instead of holding +} + +input { + kb_layout = us,dk + kb_options = grp:win_space_toggle + follow_mouse = 1 + repeat_rate = 50 + repeat_delay = 300 + accel_profile = custom + scroll_points = 0.5 1.0 1.5 2.0 2.5 + touchpad { + natural_scroll = true + scroll_factor = 0.9 + tap-to-click = false + disable_while_typing = true + } +} + +general { + gaps_in = 0 + gaps_out = 0 + border_size = 0 + layout = dwindle +} + +animations { + enabled = false +} + +dwindle { + preserve_split = true + force_split = 2 +} + +misc { + disable_hyprland_logo = true + disable_splash_rendering = true + focus_on_activate = true +} + +ecosystem { + no_update_news = true + no_donation_nag = true +} + + +# -- Environment -- +env = XCURSOR_THEME,Bibata-Modern-Classic +env = XCURSOR_SIZE,24 +env = QT_QPA_PLATFORMTHEME,qt6ct +env = QT_STYLE_OVERRIDE,Adwaita-Dark +env = XDG_SCREENSHOTS_DIR,$HOME/Pictures/Screenshots + +# -- Autostart -- +exec-once = swaybg -c '#16181a' +exec-once = GDK_BACKEND=wayland eww daemon +exec-once = systemctl --user start hyprpolkitagent +exec-once = hypridle +exec-once = hyprctl setcursor Bibata-Modern-Classic 24 + +# -- Core -- +bind = SUPER, Return, exec, kitty +bind = SUPER, D, exec, fuzzel +bind = SUPER, B, exec, eww open battery-popup +bindr = SUPER, B, exec, eww close battery-popup +bind = SUPER, Q, killactive +bind = SUPER, F, fullscreen, 1 +bind = SUPER SHIFT, Space, togglefloating +bind = CTRL, L, exec, hyprlock + +# -- Focus -- +bind = SUPER, h, movefocus, l +bind = SUPER, j, movefocus, d +bind = SUPER, k, movefocus, u +bind = SUPER, l, movefocus, r + +# -- Move windows -- +bind = SUPER SHIFT, h, movewindow, l +bind = SUPER SHIFT, j, movewindow, d +bind = SUPER SHIFT, k, movewindow, u +bind = SUPER SHIFT, l, movewindow, r + +# -- Workspaces -- +bind = SUPER, 1, workspace, 1 +bind = SUPER, 2, workspace, 2 +bind = SUPER, 3, workspace, 3 +bind = SUPER, 4, workspace, 4 +bind = SUPER, 5, workspace, 5 + +bind = SUPER SHIFT, 1, movetoworkspace, 1 +bind = SUPER SHIFT, 2, movetoworkspace, 2 +bind = SUPER SHIFT, 3, movetoworkspace, 3 +bind = SUPER SHIFT, 4, movetoworkspace, 4 +bind = SUPER SHIFT, 5, movetoworkspace, 5 + +# -- Move current workspace to another monitor -- +bind = SUPER ALT, h, movecurrentworkspacetomonitor, l +bind = SUPER ALT, l, movecurrentworkspacetomonitor, r + +# -- Resize (hold for repeat) -- +binde = SUPER CTRL, h, resizeactive, -40 0 +binde = SUPER CTRL, j, resizeactive, 0 40 +binde = SUPER CTRL, k, resizeactive, 0 -40 +binde = SUPER CTRL, l, resizeactive, 40 0 + +# -- Mouse -- +bindm = SUPER, mouse:272, movewindow +bindm = SUPER, mouse:273, resizewindow + +# -- Audio (bindel: locked + repeat; bindl: locked, no repeat) -- +bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+ +bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- +bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle +bindl = , XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle + +# -- Brightness -- +bindel = , XF86MonBrightnessUp, exec, brightnessctl set 5%+ +bindel = , XF86MonBrightnessDown, exec, brightnessctl set 5%- + +# -- Media -- +bindl = , XF86AudioPlay, exec, playerctl play-pause +bindl = , XF86AudioNext, exec, playerctl next +bindl = , XF86AudioPrev, exec, playerctl previous + +# -- Screenshots -- +# Super+S: region -> clipboard +bind = SUPER, S, exec, grimblast --notify copy area +# Super+Ctrl+S: full screen -> file + clipboard +bind = SUPER CTRL, S, exec, grimblast --notify copysave screen +# Super+Shift+S: region -> file + clipboard + open in GIMP +bind = SUPER SHIFT, S, exec, bash -c 'mkdir -p "$HOME/Pictures/Screenshots"; f="$HOME/Pictures/Screenshots/$(date +%Y-%m-%dT%H-%M-%S).png"; grimblast --notify --freeze save area "$f" && wl-copy --type image/png < "$f" && gimp "$f"' + diff --git a/config/hypr/hyprlock.conf b/config/hypr/hyprlock.conf new file mode 100644 index 0000000..971a6e3 --- /dev/null +++ b/config/hypr/hyprlock.conf @@ -0,0 +1,34 @@ +general { + hide_cursor = true + grace = 0 + disable_loading_bar = true + no_fade_in = true + no_fade_out = true +} + +background { + monitor = + color = rgb(000000) +} + +input-field { + monitor = + size = 400, 60 + outline_thickness = 20 + rounding = 0 + outer_color = rgba(ffffffff) + inner_color = rgba(000000ff) + font_color = rgb(ffffff) + font_family = JetBrainsMono Nerd Font + font_size = 16 + fade_on_empty = false + fade_timeout = 0 + placeholder_text = + hide_input = true + fail_color = rgb(ff5555) + fail_text = + capslock_color = rgb(ffb000) + position = 0, 0 + halign = center + valign = center +} diff --git a/config/nvim/init.lua b/config/nvim/init.lua new file mode 100644 index 0000000..a82dc84 --- /dev/null +++ b/config/nvim/init.lua @@ -0,0 +1,114 @@ +vim.o.winborder = "rounded" +vim.o.tabstop = 2 +vim.o.ignorecase = true +vim.o.shiftwidth = 2 +vim.o.smartindent = true +vim.opt.wrap = false +vim.opt.number = true +vim.o.relativenumber = true +vim.o.termguicolors = true +vim.o.undofile = true +vim.o.incsearch = true +vim.o.signcolumn = "yes" +vim.opt.clipboard = "unnamedplus" +vim.opt.expandtab = true + +-- On NixOS the flake provides plugins (vim.g.nix_managed set in the flake); +-- on Arch, vim.pack fetches them at runtime. Both paths keep working. +if not vim.g.nix_managed then + vim.pack.add({ + { src = "https://github.com/nvim-tree/nvim-web-devicons" }, + { src = "https://github.com/scottmckendry/cyberdream.nvim" }, + { src = "https://github.com/stevearc/oil.nvim" }, + { src = "https://github.com/neovim/nvim-lspconfig" }, + { src = "https://github.com/chomosuke/typst-preview.nvim" }, + { src = "https://github.com/saghen/blink.cmp" }, + { src = "https://github.com/dynamotn/Navigator.nvim" }, + { src = "https://github.com/folke/snacks.nvim" }, + { src = "https://github.com/folke/todo-comments.nvim" }, + { src = "https://github.com/folke/trouble.nvim" }, + { src = "https://github.com/nvim-treesitter/nvim-treesitter" }, + { src = "https://github.com/j-hui/fidget.nvim" }, + { src = "https://github.com/nvim-lualine/lualine.nvim" }, + }) +end + +require("blink.cmp").setup() +require("Navigator").setup() +require("todo-comments").setup() +require("trouble").setup() +require("fidget").setup() +require("lualine").setup({ + options = { + component_separators = { left = "|", right = "|" }, + section_separators = { left = "|", right = "|" }, + }, +}) +require("oil").setup({ + default_file_explorer = true, + delete_to_trash = true, + skip_confirm_for_simple_edits = true, + view_options = { show_hidden = true }, + keymaps = { + ["g?"] = { "actions.show_help", mode = "n" }, + [""] = "actions.select", + ["-"] = { "actions.parent", mode = "n" }, + ["_"] = { "actions.open_cwd", mode = "n" }, + ["`"] = { "actions.cd", mode = "n" }, + }, + use_default_keymaps = false, +}) +require("snacks").setup({ picker = { ui_select = true } }) + +vim.cmd("colorscheme cyberdream") +vim.api.nvim_create_autocmd("TextYankPost", { + callback = function() + vim.highlight.on_yank() + end, +}) + +vim.api.nvim_create_autocmd("FileType", { + pattern = "typst", + callback = function() + vim.opt_local.wrap = true + vim.opt_local.linebreak = true + vim.opt_local.breakindent = true + end, +}) + +vim.lsp.enable({ "lua_ls", "svelte", "tinymist", "ts_ls", "rust_analyzer", "yamlls", "html", "cssls", "jsonls", "nixd", "zls" }) +vim.api.nvim_create_autocmd("LspAttach", { + callback = function(ev) + vim.keymap.set('n', 'gd', vim.lsp.buf.definition) + vim.keymap.set('n', 'gD', vim.lsp.buf.declaration) + vim.keymap.set('n', 'gr', Snacks.picker.lsp_references) + vim.keymap.set('n', 'gt', Snacks.picker.lsp_type_definitions) + vim.keymap.set('n', 'ss', Snacks.picker.lsp_symbols) + vim.keymap.set('n', 'sS', Snacks.picker.lsp_workspace_symbols) + vim.keymap.set('n', 'ca', vim.lsp.buf.code_action) + vim.keymap.set('n', 'cr', vim.lsp.buf.rename) + end +}) + +vim.g.mapleader = " " +vim.keymap.set('n', 'o', ':update :source') +vim.keymap.set('n', 'w', ':write') +vim.keymap.set('n', 'q', ':quit') +vim.keymap.set('n', 'f', function() Snacks.picker.files() end) +vim.keymap.set('n', 'sg', function() Snacks.picker.grep() end) +vim.keymap.set('n', 'e', ':Oil') +vim.keymap.set({ 'n', 't' }, '', ':NavigatorLeft') +vim.keymap.set({ 'n', 't' }, '', ':NavigatorRight') +vim.keymap.set({ 'n', 't' }, '', ':NavigatorUp') +vim.keymap.set({ 'n', 't' }, '', ':NavigatorDown') +vim.keymap.set({ "n", "t" }, "", function() Snacks.terminal.toggle("zsh") end) -- Open Snacks picker +vim.keymap.set('n', 'g', function() Snacks.lazygit() end) +vim.keymap.set('n', 't', function() Snacks.picker.todo_comments() end) +vim.keymap.set('n', 'x', ":Trouble diagnostics toggle filter.buf=0") +vim.keymap.set('n', 'X', ":Trouble diagnostics") +vim.keymap.set('n', '|', ":vsplit") +vim.keymap.set('n', '_', ":split") +vim.keymap.set('n', '', ":noh") +vim.keymap.set("v", ">", ">gv", { silent = true, desc = "Indent and reselect" }) +vim.keymap.set("v", "<", "lf', vim.lsp.buf.format) diff --git a/config/nvim/nvim-pack-lock.json b/config/nvim/nvim-pack-lock.json new file mode 100644 index 0000000..3341b60 --- /dev/null +++ b/config/nvim/nvim-pack-lock.json @@ -0,0 +1,68 @@ +{ + "plugins": { + "Navigator.nvim": { + "rev": "d43816089689ccffd23543bc02331e9a68f3ec2f", + "src": "https://github.com/dynamotn/Navigator.nvim" + }, + "blink.cmp": { + "rev": "c573a15a62bd0bfd4006ee0849b24f5404395500", + "src": "https://github.com/saghen/blink.cmp" + }, + "catppuccin": { + "rev": "e068ab5f8261f23f6f71ffd8791ae40315b77b9c", + "src": "https://github.com/catppuccin/nvim" + }, + "copilot.lua": { + "rev": "ad7e729e9a6348f7da482be0271d452dbc4c8e2c", + "src": "https://github.com/zbirenbaum/copilot.lua" + }, + "cyberdream.nvim": { + "rev": "39f69ebcd8de69c741bb7a65353aa37dd0680ad1", + "src": "https://github.com/scottmckendry/cyberdream.nvim" + }, + "fidget.nvim": { + "rev": "889e2e96edef4e144965571d46f7a77bcc4d0ddf", + "src": "https://github.com/j-hui/fidget.nvim" + }, + "lualine.nvim": { + "rev": "a905eeebc4e63fdc48b5135d3bf8aea5618fb21c", + "src": "https://github.com/nvim-lualine/lualine.nvim" + }, + "nvim-lspconfig": { + "rev": "4b7fbaa239c5db6b36f424a4521ca9f1a401be33", + "src": "https://github.com/neovim/nvim-lspconfig" + }, + "nvim-treesitter": { + "rev": "4916d6592ede8c07973490d9322f187e07dfefac", + "src": "https://github.com/nvim-treesitter/nvim-treesitter" + }, + "nvim-web-devicons": { + "rev": "c72328a5494b4502947a022fe69c0c47e53b6aa6", + "src": "https://github.com/nvim-tree/nvim-web-devicons" + }, + "oil.nvim": { + "rev": "0fcc83805ad11cf714a949c98c605ed717e0b83e", + "src": "https://github.com/stevearc/oil.nvim" + }, + "snacks.nvim": { + "rev": "ad9ede6a9cddf16cedbd31b8932d6dcdee9b716e", + "src": "https://github.com/folke/snacks.nvim" + }, + "todo-comments.nvim": { + "rev": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668", + "src": "https://github.com/folke/todo-comments.nvim" + }, + "tree-sitter-surrealdb.nvim": { + "rev": "a875cb4be3035a0cb525c35c4c6f1512fb9fa27d", + "src": "https://github.com/dariuscorvus/tree-sitter-surrealdb.nvim" + }, + "trouble.nvim": { + "rev": "bd67efe408d4816e25e8491cc5ad4088e708a69a", + "src": "https://github.com/folke/trouble.nvim" + }, + "typst-preview.nvim": { + "rev": "87db18b8d19c8b0eed399f52e4c527ce5afe4817", + "src": "https://github.com/chomosuke/typst-preview.nvim" + } + } +} diff --git a/firmware/README.md b/firmware/README.md new file mode 100644 index 0000000..79c2967 --- /dev/null +++ b/firmware/README.md @@ -0,0 +1,25 @@ +# Asahi peripheral firmware (vendored) + +`hardware.asahi.peripheralFirmwareDirectory` points here. A **pure** flake +cannot read `/boot/asahi`, so the Wi-Fi/Bluetooth firmware must live inside the +repo tree and be git-tracked. + +## During install, on the Mac: + +```sh +cp /boot/asahi/all_firmware.tar.gz ~/.dotfiles/firmware/ +cp /boot/asahi/kernelcache* ~/.dotfiles/firmware/ +git -C ~/.dotfiles add -f firmware/all_firmware.tar.gz firmware/kernelcache* +``` + +(They're `.gitignore`-exempt via `-f` if needed — but the flake only sees +**tracked** files, so they must be added.) + +## ⚠ Keep this repository PRIVATE + +This firmware is **non-free and non-redistributable**. Do not push it to a +public remote. + +Until you copy these files in, `nixos-rebuild`/`nixos-install` will fail at the +firmware-extraction step — that's expected, same as the placeholder +`hardware-configuration.nix`. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1208f83 --- /dev/null +++ b/flake.lock @@ -0,0 +1,132 @@ +{ + "nodes": { + "apple-silicon": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1782749693, + "narHash": "sha256-Dj2m93PXWyOoXyp9tQlyjtBPbpVobKUrgnVDNNsNT1o=", + "owner": "nix-community", + "repo": "nixos-apple-silicon", + "rev": "b6ed8c114eee6739e3a2c06d99e2e19ac608dedf", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixos-apple-silicon", + "type": "github" + } + }, + "firefox-addons": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "dir": "pkgs/firefox-addons", + "lastModified": 1782878575, + "narHash": "sha256-sw0B1D//VBGXLQZJXXFLjunnMfO7hCZ5S1kv06BBO5A=", + "owner": "rycee", + "repo": "nur-expressions", + "rev": "e5d8f0375b4613d200e8efd64df2183a75290c4e", + "type": "gitlab" + }, + "original": { + "dir": "pkgs/firefox-addons", + "owner": "rycee", + "repo": "nur-expressions", + "type": "gitlab" + } + }, + "flake-compat": { + "locked": { + "lastModified": 1761640442, + "narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=", + "owner": "nix-community", + "repo": "flake-compat", + "rev": "4a56054d8ffc173222d09dad23adf4ba946c8884", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "flake-compat", + "type": "github" + } + }, + "home-manager": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1782881387, + "narHash": "sha256-HvlS1KDGXDjd1bNzzPvH1mkDJATlDAfVYfTh//wJBEA=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "d32537981c036473cf6990ae03176a5d84c43b29", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1782723713, + "narHash": "sha256-oPXCU/SSUokcGaJREHibG1CBX3+s/W7orDWQOZDsEeQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b5aa0fbd538984f6e3d201be0005b4463d8b09f8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "apple-silicon": "apple-silicon", + "firefox-addons": "firefox-addons", + "home-manager": "home-manager", + "nixpkgs": "nixpkgs", + "zen-browser": "zen-browser" + } + }, + "zen-browser": { + "inputs": { + "home-manager": [ + "home-manager" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1782812215, + "narHash": "sha256-OPVK9WW9QsO2aj1R+Ln3p7fniFj5h441vb3LyrzpeE4=", + "owner": "0xc000022070", + "repo": "zen-browser-flake", + "rev": "a7c2a9a5e492e0e62072547f7e4c2abf138425c5", + "type": "github" + }, + "original": { + "owner": "0xc000022070", + "repo": "zen-browser-flake", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0595eae --- /dev/null +++ b/flake.nix @@ -0,0 +1,208 @@ +{ + description = "mads — NixOS for Apple Silicon (mbp) and x86 (pc), one flake"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + apple-silicon = { + url = "github:nix-community/nixos-apple-silicon"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + firefox-addons = { + url = "gitlab:rycee/nur-expressions?dir=pkgs/firefox-addons"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + zen-browser = { + url = "github:0xc000022070/zen-browser-flake"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.home-manager.follows = "home-manager"; + }; + }; + + outputs = { self, nixpkgs, apple-silicon, home-manager, firefox-addons, zen-browser, ... }: + let + # ───────────────────── shared home-manager user ───────────────────── + home = { pkgs, ... }: { + imports = [ zen-browser.homeModules.beta ]; + home.stateVersion = "25.11"; + + # Raw configs that stay files (no good native module). + xdg.configFile = { + "hypr".source = ./config/hypr; + "eww".source = ./config/eww; + "fuzzel".source = ./config/fuzzel; + }; + + # Dark GTK apps (gimp, file dialogs). + gtk = { + enable = true; + theme = { name = "Adwaita-dark"; package = pkgs.gnome-themes-extra; }; + gtk3.extraConfig.gtk-application-prefer-dark-theme = 1; + gtk4.extraConfig.gtk-application-prefer-dark-theme = 1; + }; + + # Neovim: init.lua verbatim; plugins + LSP pinned by Nix. + programs.neovim = { + enable = true; + withPython3 = false; + withRuby = false; + withNodeJs = false; + initLua = "vim.g.nix_managed = true\n" + + builtins.readFile ./config/nvim/init.lua; + plugins = with pkgs.vimPlugins; [ + nvim-web-devicons cyberdream-nvim oil-nvim nvim-lspconfig + typst-preview-nvim blink-cmp Navigator-nvim + snacks-nvim todo-comments-nvim trouble-nvim + nvim-treesitter.withAllGrammars + fidget-nvim lualine-nvim + ]; + extraPackages = with pkgs; [ + lua-language-server rust-analyzer tinymist nixd zls + yaml-language-server vscode-langservers-extracted + typescript-language-server svelte-language-server + ]; + }; + + # Zen browser + Tridactyl, extensions pinned (works on aarch64 + x86). + programs.zen-browser = { + enable = true; + setAsDefaultBrowser = true; + profiles.default = { + settings = { + "browser.newtabpage.enabled" = false; + "privacy.donottrackheader.enabled" = true; + "signon.rememberSignons" = false; + }; + extensions.packages = with firefox-addons.packages.${pkgs.stdenv.hostPlatform.system}; [ + tridactyl + ublock-origin + ]; + }; + }; + + # zsh + starship, no framework. + programs.zsh = { + enable = true; + autosuggestion.enable = true; + syntaxHighlighting.enable = true; + shellAliases = { ll = "ls -alh"; gs = "git status"; }; + }; + programs.starship.enable = true; + + home.packages = with pkgs; [ ripgrep fd tridactyl-native ]; + }; + + # ───────────────────── shared system (both hosts) ───────────────────── + common = { pkgs, ... }: { + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + networking.networkmanager.enable = true; + networking.networkmanager.wifi.backend = "iwd"; + + time.timeZone = "Europe/Copenhagen"; # set yours + i18n.defaultLocale = "en_US.UTF-8"; + + programs.hyprland = { enable = true; withUWSM = true; }; + programs.hyprlock.enable = true; + services.hypridle.enable = true; + xdg.portal = { + enable = true; + extraPortals = [ pkgs.xdg-desktop-portal-hyprland ]; + }; + + # greetd autologins mads straight into Hyprland (via uwsm). + services.greetd = { + enable = true; + settings = { + initial_session = { user = "mads"; command = "uwsm start hyprland"; }; + default_session = { user = "mads"; command = "uwsm start hyprland"; }; + }; + }; + + hardware.bluetooth.enable = true; + programs.zsh.enable = true; + fonts.packages = [ pkgs.nerd-fonts.jetbrains-mono ]; + + environment.systemPackages = with pkgs; [ + git kitty fuzzel gimp + wl-clipboard grim slurp swaybg brightnessctl playerctl grimblast + hyprpolkitagent xdg-utils + ]; + + users.users.mads = { + isNormalUser = true; + extraGroups = [ "wheel" "networkmanager" "video" ]; + shell = pkgs.zsh; + initialPassword = "changeme"; # CHANGE after first boot (`passwd`) + }; + + system.stateVersion = "25.11"; + + home-manager.useGlobalPkgs = true; + home-manager.useUserPackages = true; + home-manager.users.mads = home; + }; + + mkHost = { system, modules }: nixpkgs.lib.nixosSystem { + inherit system; + modules = [ home-manager.nixosModules.home-manager common ] ++ modules; + }; + in + { + nixosConfigurations = { + # ── Apple Silicon MacBook (Asahi) ── + mbp = mkHost { + system = "aarch64-linux"; + modules = [ + apple-silicon.nixosModules.apple-silicon-support + ./hosts/mbp/hardware-configuration.nix # generated on the Mac + ({ ... }: { + networking.hostName = "mbp"; + + # Wi-Fi/BT firmware, vendored (a pure flake can't read /boot/asahi). + hardware.asahi.peripheralFirmwareDirectory = ./firmware; + + boot.loader.systemd-boot.enable = true; + boot.extraModprobeConfig = "options hid_apple fnmode=1"; + boot.blacklistedKernelModules = [ "pcspkr" ]; + + # Asahi kernel + Mesa cache (else compile from source). + nix.settings = { + extra-substituters = [ "https://nixos-apple-silicon.cachix.org" ]; + extra-trusted-public-keys = [ + "nixos-apple-silicon.cachix.org-1:8psDu5SA5dAD7qA0zMy5UT292TxeEPzIz8VVEr2Js20=" + ]; + }; + }) + ]; + }; + + # ── Arbitrary x86_64 PC ── + pc = mkHost { + system = "x86_64-linux"; + modules = [ + ./hosts/pc/hardware-configuration.nix # generated on the PC + ({ ... }: { + networking.hostName = "pc"; + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; # normal x86 UEFI + }) + ]; + }; + }; + + # `nix develop` on either arch. + devShells = nixpkgs.lib.genAttrs [ "aarch64-linux" "x86_64-linux" ] (system: { + default = nixpkgs.legacyPackages.${system}.mkShell { + packages = [ nixpkgs.legacyPackages.${system}.nodejs ]; + }; + }); + }; +} diff --git a/hosts/mbp/hardware-configuration.nix b/hosts/mbp/hardware-configuration.nix new file mode 100644 index 0000000..32e2e3e --- /dev/null +++ b/hosts/mbp/hardware-configuration.nix @@ -0,0 +1,14 @@ +# DUMMY hardware config — just enough for the flake to `nix eval`/build on a +# non-target machine. It is OVERWRITTEN at install: +# +# nixos-generate-config --root /mnt +# cp /mnt/etc/nixos/hardware-configuration.nix ~/nixos/ +# +# The real file has your actual disk UUIDs and the Asahi EFI/boot mounts. +{ ... }: +{ + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; +} diff --git a/hosts/pc/hardware-configuration.nix b/hosts/pc/hardware-configuration.nix new file mode 100644 index 0000000..0f4c551 --- /dev/null +++ b/hosts/pc/hardware-configuration.nix @@ -0,0 +1,14 @@ +# DUMMY x86 hardware config — just enough for `nix eval`/build off-target. +# OVERWRITTEN at install by: nixos-generate-config --root /mnt +# then copy /mnt/etc/nixos/hardware-configuration.nix here. +{ ... }: +{ + fileSystems."/" = { + device = "/dev/disk/by-label/nixos"; + fsType = "ext4"; + }; + fileSystems."/boot" = { + device = "/dev/disk/by-label/BOOT"; + fsType = "vfat"; + }; +}