mirror of
https://github.com/nmasur/dotfiles
synced 2026-09-14 19:58:09 +00:00
Compare commits
182
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db49e746b8 | ||
|
|
7f8400ff58 | ||
|
|
d3914768c8 | ||
|
|
fd9cae9c40 | ||
|
|
f593fdb81f | ||
|
|
01ee98155a | ||
|
|
2a3cbfb5f5 | ||
|
|
ca1343f593 | ||
|
|
a053a9ab0f | ||
|
|
90dc80f7b4 | ||
|
|
aff596aa64 | ||
|
|
6e0f4e2000 | ||
|
|
72c4f4823b | ||
|
|
a93fa75779 | ||
|
|
a50d2c01a9 | ||
|
|
a72e81fcce | ||
|
|
fe2843ead6 | ||
|
|
68320577ae | ||
|
|
38ba019c3d | ||
|
|
e339a4969f | ||
|
|
44f2ca803e | ||
|
|
239b56c1b1 | ||
|
|
4ba1ed807c | ||
|
|
32989f1758 | ||
|
|
b0b08d2475 | ||
|
|
5ecefd4589 | ||
|
|
fffb9d547c | ||
|
|
3c303ccd19 | ||
|
|
d222fef02e | ||
|
|
71ce9689ce | ||
|
|
4047cae211 | ||
|
|
e70661a623 | ||
|
|
a979036462 | ||
|
|
8ae7fd435e | ||
|
|
2d5de254e4 | ||
|
|
d90b45d194 | ||
|
|
d8a29f0267 | ||
|
|
f3a8d45722 | ||
|
|
df38163999 | ||
|
|
31cecf05cc | ||
|
|
59685bf023 | ||
|
|
a3a5c516ce | ||
|
|
033026bff8 | ||
|
|
5929b3201d | ||
|
|
373beb6daf | ||
|
|
8cef9bfb59 | ||
|
|
98d426ce89 | ||
|
|
e107431fa9 | ||
|
|
1960d84e7a | ||
|
|
671a17af76 | ||
|
|
71709776f9 | ||
|
|
829af86dd8 | ||
|
|
e7fd5eb770 | ||
|
|
f0b400d134 | ||
|
|
573c0acd33 | ||
|
|
eec777c0c1 | ||
|
|
638b112db9 | ||
|
|
27917ffede | ||
|
|
73b62e0907 | ||
|
|
b25cfe6877 | ||
|
|
d162df2515 | ||
|
|
396337f74d | ||
|
|
76b50920de | ||
|
|
e04cdab409 | ||
|
|
d238b7a81a | ||
|
|
f3e24c3730 | ||
|
|
5862c7cb29 | ||
|
|
a175acd0ad | ||
|
|
3f3cf65c77 | ||
|
|
35d40ca346 | ||
|
|
e0fb590314 | ||
|
|
2f107ad207 | ||
|
|
b6b0a29a78 | ||
|
|
fd6da7e68d | ||
|
|
28049e9c30 | ||
|
|
d95cdb1385 | ||
|
|
cf8f1fa221 | ||
|
|
8b098f0589 | ||
|
|
12961b19c0 | ||
|
|
455c047607 | ||
|
|
fa307c0f4b | ||
|
|
07a980e937 | ||
|
|
0a4a28d06a | ||
|
|
5fd6593a92 | ||
|
|
8af595b62b | ||
|
|
1e723db068 | ||
|
|
16e42c2021 | ||
|
|
38edf0a1a6 | ||
|
|
15f70dce2c | ||
|
|
1a7633fe8c | ||
|
|
40025b1211 | ||
|
|
f5d9664caf | ||
|
|
7010d6cea3 | ||
|
|
a2a429d286 | ||
|
|
7f5b5eb809 | ||
|
|
f5f3a8336e | ||
|
|
4444a479d4 | ||
|
|
395c4b4348 | ||
|
|
8dbec5f6ab | ||
|
|
f8d79cdf3a | ||
|
|
280082acfe | ||
|
|
1edf9da155 | ||
|
|
8d970103e1 | ||
|
|
d9137ad738 | ||
|
|
4eacca970e | ||
|
|
d09ee3337b | ||
|
|
22e1c9b8eb | ||
|
|
acd71623e4 | ||
|
|
9a97d94ffa | ||
|
|
84bffd13e8 | ||
|
|
774b323837 | ||
|
|
2f2d2c0145 | ||
|
|
6b17248804 | ||
|
|
a7dacb7edf | ||
|
|
dfd3b955c0 | ||
|
|
fd7ddf71f3 | ||
|
|
bba66e3e6a | ||
|
|
be6d6b0d35 | ||
|
|
0239a9925c | ||
|
|
49e35403b6 | ||
|
|
430b522c61 | ||
|
|
a64488093c | ||
|
|
54d2376437 | ||
|
|
cd0a5d5de0 | ||
|
|
10eecfa136 | ||
|
|
b5b3f6cb6a | ||
|
|
810c92a7b8 | ||
|
|
fcb28c8151 | ||
|
|
e2a2d3de14 | ||
|
|
adbc2bd261 | ||
|
|
117fd8a06e | ||
|
|
d9fbdf7bfe | ||
|
|
2d13780d2e | ||
|
|
53a712a217 | ||
|
|
31d34c6540 | ||
|
|
a978e67346 | ||
|
|
a6915a6d2d | ||
|
|
44c4034236 | ||
|
|
a99c14a8c2 | ||
|
|
f1c8e26044 | ||
|
|
29cd253d9c | ||
|
|
603b9a2eff | ||
|
|
9c35744ab9 | ||
|
|
7d6b29c30b | ||
|
|
fd589b66ef | ||
|
|
853aa644fa | ||
|
|
b6e7221cc6 | ||
|
|
235901b3b0 | ||
|
|
7cd74986df | ||
|
|
a6e15b71cd | ||
|
|
2e4467b6ea | ||
|
|
431ebd732c | ||
|
|
2d576bd25d | ||
|
|
a1615eda67 | ||
|
|
444582a5a5 | ||
|
|
fbabdadb32 | ||
|
|
f8dfa2a817 | ||
|
|
e4a8ef15ce | ||
|
|
5c922310f5 | ||
|
|
2f274cd652 | ||
|
|
407e9c3af8 | ||
|
|
c13e029805 | ||
|
|
c56bc30200 | ||
|
|
6064fdb0e0 | ||
|
|
5cf27d6d66 | ||
|
|
ceca1ffd5a | ||
|
|
ebbc9c5a5d | ||
|
|
aea3e95ef5 | ||
|
|
d79c1ba7df | ||
|
|
422131134a | ||
|
|
5d8e10b814 | ||
|
|
fdd5eb6418 | ||
|
|
72c548e707 | ||
|
|
a57207dd5d | ||
|
|
94b4217be3 | ||
|
|
7619ac6ea2 | ||
|
|
7543721020 | ||
|
|
bf52069886 | ||
|
|
839ca079c2 | ||
|
|
85f09b1126 | ||
|
|
29c7a27135 | ||
|
|
3523f20665 |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"WebFetch(domain:github.com)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"Bash(gh run *)",
|
||||
"Bash(jj status *)",
|
||||
"Bash(jj new *)",
|
||||
"Bash(jj describe *)",
|
||||
"Bash(nix flake *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-09-06
|
||||
|
||||
- **Configured OpenID Connect (OIDC) authentication for Immich**:
|
||||
- Configured `services.immich.settings.oauth` with OIDC settings pointing to Pocket ID (`auth.masu.rs`), using client ID `1f4e0f8d-6cee-4d67-8d53-74bf6c18ae09`.
|
||||
- Added secret management for `immich-oidc-secret.age` via `secrets.immich-oidc-secret` with owner `immich` and group `shared` (0440).
|
||||
- Wired client secret substitution using NixOS's native `clientSecret._secret = config.secrets.immich-oidc-secret.dest`, leveraging `utils.genJqSecretsReplacement` with systemd `LoadCredential`.
|
||||
- Configured `systemd.services.immich-server` to order after `immich-oidc-secret-secret.service`.
|
||||
- Updated `docs/oidc-services.md` with the verified configuration and redirect URIs.
|
||||
|
||||
- **Fixed OpenSSH authorized principals certificate authentication for Cloudflare Tunnel**:
|
||||
- Replaced manual `environment.etc."ssh/authorized_principals/${username}"` symlink and `Match User` config with NixOS native `users.users.<name>.openssh.authorizedPrincipals`.
|
||||
- Root cause: `environment.etc` without an explicit `mode` creates symlinks pointing into `/nix/store`, which has group-writable mode `0775` (`nixbld` group). Under `StrictModes yes`, sshd refused authentication with `bad ownership or modes for directory /nix/store`, causing certificate principal matching to fail with `Certificate does not contain an authorized principal`.
|
||||
- Setting `users.users.<name>.openssh.authorizedPrincipals` causes NixOS to generate `/etc/ssh/authorized_principals.d/<name>` with `mode = "0444"`, copying the file instead of symlinking into the store, and automatically configuring `services.openssh.settings.AuthorizedPrincipalsFile = "/etc/ssh/authorized_principals.d/%u"`.
|
||||
- Also added `mode = "0444"` to `/etc/ssh/ca.pub` and moved `TrustedUserCAKeys` into `services.openssh.settings`.
|
||||
|
||||
- **Configured OpenID Connect (OIDC) authentication for Mealie**:
|
||||
- Configured `services.mealie.settings` with OIDC settings pointing to Pocket ID (`auth.masu.rs`), using client ID `040925ed-b39e-4442-b8e8-369c948c0cd2`.
|
||||
- Added secret management for `mealie-oidc-secret.age` via `secrets.mealie-oidc-secret`, using `prefix = "OIDC_CLIENT_SECRET="` to generate an environment file.
|
||||
- Configured `services.mealie.credentialsFile` to load the client secret via systemd's `EnvironmentFile` without exposing it in the world-readable Nix store or systemd unit file.
|
||||
- Configured `systemd.services.mealie` to order after `mealie-oidc-secret-secret.service`.
|
||||
- Updated `docs/oidc-services.md` with the verified configuration and callback URI (`https://cooking.masu.rs/login`).
|
||||
|
||||
- **Configured OpenID Connect (OIDC) authentication for Actual Budget**:
|
||||
- Configured `services.actual.settings` with `loginMethod = "openid"` and `openId` settings pointing to Pocket ID (`auth.masu.rs`), using client ID `92afe9f8-7ef6-42ab-8a06-701df3c7179d`.
|
||||
- Added secret management for `actualbudget-oidc-secret.age` via `secrets.actualbudget-oidc-secret`, set with owner `actualbudget` and group `shared` (0440).
|
||||
- Configured `systemd.services.actual` to order after the decrypted secret service and granted the dynamic unit access via `SupplementaryGroups = [ "shared" ]` and `PrivateUsers = false`.
|
||||
- Updated `docs/oidc-services.md` with the verified callback URI (`https://money.masu.rs/openid/callback`) and NixOS configuration snippet.
|
||||
|
||||
## 2026-08-29 (root cause found and fixed)
|
||||
|
||||
- **Root-caused and fixed the recurring post-TUI typing lag** (fish + Zellij + Ghostty) using a `lag-triage` capture from a live lagging shell plus a deterministic PTY reproduction (`presets/programs/lag-triage/upstream_repro.py`):
|
||||
- **Root cause chain**: (1) fish latches feature flags from its **startup environment**, before `config.fish` runs — so the existing `set -gx fish_features no-query-term` in `shellInit` never applied to the shell that set it, only to its children. (2) Zellij spawns pane shells via `default_shell` with no `fish_features` in the environment, so every pane's fish latched `query-term` **on** (the fish 4.8.1 default; the triage log from the lagging shell confirmed `query-term on` while `$fish_features` was correctly set to `no-query-term`). (3) With query-term on, fish sends OSC 11 + CPR (`\e[6n`) + DA1 (`\e[0c`) after **every** command and waits for replies relayed by Zellij. (4) Reproduced on fish 4.8.1: if the terminal fails to reply during just **one** such cycle — answering everything before and after — that fish process's interactive reader is **permanently degraded** (keystroke echo >3s, never recovers; ~35ms before). In production Zellij drops/mangles a relay during TUI teardown or heavy output (cf. zellij-org/zellij#5158), e.g. after `nh home switch`, nvim, jjui, yazi.
|
||||
- **Why every previous observation finally makes sense**: subshells and `exec fish` were never "resetting" anything — they *inherited* the exported `fish_features=no-query-term` from config.fish, latched query-term off at startup, and were therefore **immune**. The parent zellij-spawned shell never had the variable at startup and stayed vulnerable. Raw keystroke capture in the lagging pane showed instant plain bytes (input path fine) and no stuck terminal modes — the damage was inside the fish process, exactly as the repro shows.
|
||||
- **Fix**: `zellij.nix` now spawns panes through a `fish-no-query-term` wrapper (`export fish_features=no-query-term; exec fish`), so the feature is latched off in every pane shell. Verified: interactive fish through the built wrapper with the real config reports `query-term off`; the PTY repro with `no-query-term` in the environment shows ~35ms echo through all failure phases.
|
||||
- **Correction** to the earlier 2026-08-29 entry: `query-term` does **not** default to off in fish 4.8.1 — it defaults on; it only *appeared* off in non-interactive checks because the user config's `set -gx` takes effect for `fish -c` (no reader latch) but not for interactive shells.
|
||||
- Upstream: fish-shell should bound the reader's wait for query replies instead of degrading permanently (repro script kept at `presets/programs/lag-triage/upstream_repro.py` for filing); Zellij's reply relaying is the trigger (zellij-org/zellij#5158).
|
||||
- `lag-triage` now checks `status features` and calls out `query-term on` as the known root cause, and warns that its `read`-prompt typing tests may not exhibit main-commandline lag (which produced a false "fixed by stage A" in the first capture).
|
||||
|
||||
## 2026-08-29 (later)
|
||||
|
||||
- Added a diagnostic toolkit (`lag-triage` / `unlag` fish functions + `term-probe` binary, `presets/programs/lag-triage/`) for the still-recurring post-TUI typing lag in fish + Zellij + Ghostty, instead of another blind fix. Findings that motivated it:
|
||||
- All three prior fixes were either no-ops or insufficient: `fish_features = no-query-term` is a **no-op** because `query-term` already defaults to *off* in fish 4.8.1 (verified with `status features`); disabling Ghostty's fish integration inside Zellij and setting `support_kitty_keyboard_protocol = false` did not stop recurrence.
|
||||
- PTY captures of fish 4.8.1 (`TERM=xterm-256color`, with and without `$ZELLIJ`) show fish never writes Kitty keyboard sequences to the wire — it uses modifyOtherKeys (`\e[>4;1m`), application keypad (`\e=`), bracketed paste (`?2004`), and color-theme reporting (`?2031`), enabling them at every prompt and disabling them before every external command. Crucially, a fresh subshell's startup bytes are identical to the parent's post-command re-enable bytes, so "a subshell fixes the lag" cannot be explained by a simple terminal-state reset — leaving two competing hypotheses that only live capture can separate: (1) fish-internal reader state poisoned by stray/partial escape bytes (e.g. leaked from a closing floating pane), cleared only by a new fish process; (2) Zellij/Ghostty-level stuck state (Zellij 0.45's `StdinAnsiParser` is already a proven source of input delays — see the Alt-Shift-P fix below).
|
||||
- Also note: the floating-pane TUIs (jjui via Alt-Shift-J, yazi via Alt-Shift-Y, scrollback editor) run in their own panes and never pass through the shell's fish process at all, while `nvim` runs inside the shell pane — the triage log records which path preceded the lag.
|
||||
- **Next occurrence: run `lag-triage` in the lagging shell BEFORE starting a new shell.** It snapshots the environment, queries pane terminal state (kitty flags, modifyOtherKeys, DEC modes, DA1 round-trip latency), captures raw keystroke bytes+timing bypassing fish, then applies staged resets (kitty pop/clear, modifyOtherKeys off, keypad/cursor, mouse/focus/sync, altscreen, stty, DECSTR) — the stage that cures it names the stuck layer. Logs to `~/.local/state/lag-triage/` for an upstream issue. `unlag` is the one-shot convenience version (if `unlag` never helps but `exec fish` does, the bug is fish-internal).
|
||||
|
||||
## 2026-08-29
|
||||
|
||||
- Fixed 1.5-second latency when pressing `Alt-Shift-P` to trigger `zellij-session` in Zellij 0.45.0 + Ghostty:
|
||||
- **Root Cause**: Zellij 0.45.0 introduced `StdinAnsiParser` (`zellij-client/src/stdin_ansi_parser.rs`) using `termwiz::InputParser` to parse ANSI control strings (OSCs, CSIs, DCSs) arriving on stdin. When pressing `Alt-Shift-P` (Option-Shift-P) with `support_kitty_keyboard_protocol = false`, Ghostty sent `\x1bP` (`ESC` + uppercase `P`). In ECMA-48 / VT100 standards, `ESC P` is the 7-bit ASCII representation of `DCS` (Device Control String). `StdinAnsiParser` buffered `\x1bP` waiting for a DCS string payload and string terminator (`ST` / `\x1b\`), hitting a ~1.5-second escape timeout before flushing `\x1bP` as residue to the keyboard handler.
|
||||
- **Fix**: Added `alt+shift+p=text:\x1b[112;4u` and `super+shift+p=text:\x1b[112;4u` in `ghostty.nix` to send the explicit CSI-u sequence for `Alt+Shift+p` (`'p'` with modifier 4 = `ALT | SHIFT`). `StdinAnsiParser` immediately recognizes `\x1b[112;4u` as non-DCS input and passes it straight to the keyboard handler with 0ms latency.
|
||||
|
||||
## 2026-08-26
|
||||
|
||||
- Fixed macOS shortcuts (`Cmd+T`, `Ctrl+Tab`, `Cmd+Shift+]`, `Cmd+Shift+[`, `Cmd+K`, `Cmd+Shift+E`) in Zellij + Ghostty after disabling the Kitty keyboard protocol:
|
||||
- Mapped Ghostty keybindings (`super+t`, `super+shift+]`, `super+shift+[`, `ctrl+tab`, `ctrl+shift+tab`, `super+k`, `super+shift+e`) to send standard `Alt` (`ESC`-prefix) text sequences (`\x1bt`, `\x1b}`, `\x1b{`, `\x1bK`, `\x1bE`).
|
||||
- Added matching `Alt` keybindings (`Alt t`, `Alt ]`, `Alt }`, `Alt [`, `Alt {`, `Alt Shift k`, `Alt Shift e`) in `zellij.nix` for tab creation, tab navigation, scroll mode, and scrollback editing. Symbols like `]` and `}` are parsed by Zellij's termwiz input engine as distinct character codes (`'}'` vs `']'`), so binding both `Alt }` and `Alt Shift ]` ensures `\x1b}` triggers tab navigation correctly.
|
||||
- Keeps Kitty keyboard protocol disabled in Zellij (`support_kitty_keyboard_protocol = false`) so no CSI-u flags leak into Fish shell, guaranteeing zero post-TUI typing lag while restoring all shortcuts.
|
||||
|
||||
- Fixed persistent Fish typing lag after long TUI sessions (Neovim, jjui, Yazi) inside Zellij + Ghostty, which the `no-query-term` / Ghostty-integration fixes from 2026-08-25 did not resolve:
|
||||
- Verified on Fish 4.8.1 that the `query-term` feature already defaults to `off`, so exporting `fish_features = no-query-term` is a no-op on this Fish version — it isn't the cause of (or fix for) this class of lag.
|
||||
- Set `support_kitty_keyboard_protocol = false` in `zellij.nix`. Zellij and Ghostty have several open upstream bugs (zellij-org/zellij#3887, #3723, #4178) where the Kitty keyboard protocol's enhancement-flag stack is left in an elevated state after a full-screen TUI exits without properly popping it. Every subsequent keystroke then arrives as a CSI-u sequence that Fish must wait out an escape-disambiguation timeout to parse, which reads as typing lag that worsens the longer the TUI session ran, and persists until the pane's protocol state resets (e.g. a fresh shell/pane). Disabling the protocol support in Zellij avoids the whole bug class; trades off precise modifier reporting (e.g. distinguishing Ctrl+Shift+key) for TUIs running inside Zellij panes, which this setup doesn't otherwise depend on (Shift+Enter is handled via a literal Ghostty `text:` keybind, not the Kitty protocol).
|
||||
|
||||
## 2026-08-25
|
||||
|
||||
- Fixed Nix evaluation warnings for `stdenv` deprecation and `gemini-cli`:
|
||||
- Replaced deprecated `stdenv.isLinux` and `stdenv.isDarwin` checks across module presets and package definitions with `stdenv.hostPlatform.isLinux` and `stdenv.hostPlatform.isDarwin`.
|
||||
- Replaced deprecated `pkgs.gemini-cli` with `pkgs.antigravity-cli` (and updated binary invocation to `agy`) in `experimental.nix` profile and `daily-summary.nix` launchd service.
|
||||
|
||||
## 2026-08-25
|
||||
|
||||
- Fixed multi-second hang and permanent typing latency in Fish after exiting TUIs inside Zellij and Ghostty:
|
||||
- Exported `fish_features = "no-query-term"` in `home.sessionVariables` and added `set -gx fish_features no-query-term` to Fish's top-level `shellInit`. Previous attempt (`set -a fish_features no-query-term` in `interactiveShellInit`) set a local variable inside an anonymous initialization function block that went out of scope immediately after startup. Furthermore, Fish reads `fish_features` at binary launch before interactive init functions run. Without `no-query-term` exported prior to Fish startup, Fish attempted terminal feature queries (Primary Device Attributes `DA1` / `\e[?c` and termcap) whenever a TUI (e.g. Neovim, Lazygit, Yazi) exited and returned control to Fish. Zellij drops or delays DA1 response sequences, causing Fish to block on a multi-second stdin timeout, followed by severe input reader desynchronization and typing latency on every subsequent keystroke.
|
||||
- Disabled `programs.ghostty.enableFishIntegration` and conditionally sourced Ghostty's shell integration script in `shellInit` only when NOT running inside a multiplexer (`not set -q ZELLIJ` and `not set -q TMUX`). Sourcing Ghostty's shell integration inside Zellij sent duplicate and conflicting OSC 133 prompt markers and DECSCUSR cursor escape sequences to Zellij's PTY parser.
|
||||
|
||||
## 2026-08-16
|
||||
|
||||
- Fixed Zellij new tab directory tracking by adding `__fish_update_cwd_osc` override in `presets/programs/zellij.nix`. Fish's default OSC 7 sequence includes `$hostname`, which on macOS or dynamic network environments evaluates to `Noah-MacBook-Pro.local` or a domain suffix. Zellij compares the OSC 7 hostname against its system hostname (`Noah-MacBook-Pro`), finds a mismatch, and silently ignores the CWD update, leaving new tabs stuck in a previous directory or session default. Overriding `__fish_update_cwd_osc` to send `file://<PWD>` (empty hostname) ensures Zellij always updates its cached CWD on every `cd` and prompt render.
|
||||
- Fixed Firefox "profile cannot be loaded" error on macOS by removing `home.file."Library/Application Support/Firefox/installs.ini"`. Hardcoding an installation hash in `installs.ini` broke whenever Firefox was updated or rebuilt in the Nix store because the nix store path changed, causing Firefox to compute a new installation hash, fail to match or write to the read-only `installs.ini` symlink, and error out. Firefox on macOS uses `profiles.ini` (managed by Home Manager) and `MOZ_LEGACY_PROFILES=1` (exported by nixpkgs' launcher wrapper).
|
||||
|
||||
## 2026-08-03
|
||||
|
||||
- Added `presets/security/corporate-ca.nix` (nix-darwin) and enabled it on the
|
||||
`lookingglass` host to trust a corporate TLS-intercepting proxy's root CA.
|
||||
Behind the corp network, Nix fetches failed with `SSL peer certificate ...
|
||||
self-signed certificate in certificate chain (19)` because Nix's stock Mozilla
|
||||
CA bundle doesn't contain the interception root. The module appends the cert
|
||||
to `security.pki.certificateFiles`, which rebuilds
|
||||
`/etc/ssl/certs/ca-certificates.crt` (read by both the Nix daemon and, via
|
||||
`NIX_SSL_CERT_FILE`, client-side flake fetches).
|
||||
|
||||
The cert is kept **out of this public repo** and referenced by absolute path.
|
||||
It is passed as a string (not a Nix path literal) so pure flake evaluation
|
||||
doesn't read it at eval time, and it lives at a root-owned, world-readable
|
||||
path because the unprivileged `nixbld` build user cannot traverse `$HOME`
|
||||
(mode `0750`) to read it at build time.
|
||||
|
||||
One-time setup on a machine behind the proxy:
|
||||
|
||||
```sh
|
||||
# 1. Extract the self-signed corporate root from any TLS connection it MITMs
|
||||
# (the last cert in the chain, subject == issuer). Any HTTPS host works:
|
||||
echo | openssl s_client -connect example.com:443 -servername example.com \
|
||||
-showcerts 2>/dev/null \
|
||||
| awk '/BEGIN CERT/{c++} c==2' > /tmp/CorpCA.pem
|
||||
openssl x509 -in /tmp/CorpCA.pem -noout -subject -issuer # sanity check
|
||||
|
||||
# 2. Install to the root-owned path the config points at:
|
||||
sudo install -d -m 0755 -o root -g wheel /etc/ssl/corp-ca
|
||||
sudo install -m 0644 -o root -g wheel /tmp/CorpCA.pem \
|
||||
/etc/ssl/corp-ca/CorpCA.pem
|
||||
|
||||
# 3. Bootstrap the first rebuild (which must fetch inputs over the proxy)
|
||||
# with a combined bundle, then it's permanent:
|
||||
cat /etc/ssl/certs/ca-certificates.crt /etc/ssl/corp-ca/CorpCA.pem \
|
||||
> /tmp/combined-ca.crt
|
||||
NIX_SSL_CERT_FILE=/tmp/combined-ca.crt nh darwin switch . --configuration lookingglass
|
||||
```
|
||||
|
||||
- Fixed the `zellij-session` fish function in `presets/programs/zellij.nix`
|
||||
truncating the session name (derived from the target directory basename)
|
||||
to 20 characters. Zellij names each session's Unix-domain IPC socket
|
||||
`$TMPDIR/zellij-<uid>/<version>/<name>`, and on macOS the socket path is
|
||||
capped at 103 bytes. The `/var/folders/...` `$TMPDIR` prefix consumes ~79
|
||||
of those, leaving only ~24 chars for the name, so switching into directories
|
||||
with long basenames overflowed the socket path. Because `switch-session`
|
||||
had already detached from the current session by the time the new one failed
|
||||
to bind, the failure took down the entire terminal instead of erroring
|
||||
gracefully.
|
||||
|
||||
## 2026-09-06
|
||||
|
||||
- Configured Grafana OIDC authentication via Pocket ID in `platforms/nixos/modules/nmasur/presets/services/grafana/grafana.nix`.
|
||||
- Enabled `auth.oauth_allow_insecure_email_lookup = true` in Grafana settings to allow linking an incoming OAuth login to an existing Grafana user account with the same email.
|
||||
|
||||
## 2026-07-25
|
||||
|
||||
- Added jjui config generation to `jujutsu.nix` in Home Manager to include custom GitHub ruleset bypass commands (`ctrl+b` and `ctrl+shift+b`).
|
||||
- Added `overlays/cheetah3.nix` to disable `pythonMetadataCheckPhase` for `cheetah3`.
|
||||
This fixes an issue where the NixOS rebuild fails for `sabnzbd` due to `importlib.metadata.PackageNotFoundError: No package metadata was found for cheetah3` during the Python package evaluation in `nixos-unstable`.
|
||||
|
||||
## 2026-07-20
|
||||
|
||||
- Added `overlays/paho-mqtt.nix` to disable paho-mqtt's flaky, socket-based
|
||||
test suite. Its `checkPhase` hangs in the Nix sandbox and times out with a
|
||||
`KeyboardInterrupt` after ~150s, which was breaking the `flame` rebuild
|
||||
(paho-mqtt is pulled in transitively, e.g. via mealie). The override is
|
||||
applied through `pythonPackagesExtensions` so it covers every Python
|
||||
package set.
|
||||
@@ -0,0 +1,247 @@
|
||||
# OIDC Service Analysis & Setup Requirements
|
||||
|
||||
This document analyzes the services defined in this repository (specifically within `platforms/nixos/modules/nmasur/presets/services/` and related server profiles like `flame` and `swan`), evaluates their OpenID Connect (OIDC) compatibility, and details the requirements for configuring OIDC logins.
|
||||
|
||||
---
|
||||
|
||||
## Identity Provider Context: Pocket ID
|
||||
|
||||
The configuration already includes a central self-hosted identity provider: **Pocket ID** (`platforms/nixos/modules/nmasur/presets/services/pocket-id/pocket-id.nix`), hosted on the communications server (`flame`) behind Caddy at **`https://auth.masu.rs`**.
|
||||
|
||||
Pocket ID is an OpenID Connect (OIDC) provider with WebAuthn/Passkey support. It exposes the following standard OIDC endpoints:
|
||||
- **Issuer URL:** `https://auth.masu.rs`
|
||||
- **Discovery Endpoint:** `https://auth.masu.rs/.well-known/openid-configuration`
|
||||
- **Authorization Endpoint:** `https://auth.masu.rs/authorize`
|
||||
- **Token Endpoint:** `https://auth.masu.rs/api/oidc/token`
|
||||
- **Userinfo Endpoint:** `https://auth.masu.rs/api/oidc/userinfo`
|
||||
- **JWKS Endpoint:** `https://auth.masu.rs/.well-known/jwks.json`
|
||||
|
||||
---
|
||||
|
||||
## 1. Service Compatibility Breakdown
|
||||
|
||||
### Tier 1: First-Class / Native OIDC Support
|
||||
|
||||
These services support OpenID Connect natively without requiring external authentication proxies or custom code:
|
||||
|
||||
| Service | Hostname | OIDC Support Level | Configuration Method |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Immich** | `photos.masu.rs` | Native core feature | NixOS config (`services.immich.settings.oauth`) |
|
||||
| **Gitea** | `git.masu.rs` | Native core feature | NixOS config / CLI or Web UI |
|
||||
| **Nextcloud** | `cloud.masu.rs` | Native (official `user_oidc` app) | Nextcloud app + `nextcloud-occ user_oidc:provider` |
|
||||
| **Grafana** | `metrics.masu.rs` | Native (Generic OAuth) | NixOS config (`services.grafana.settings."auth.generic_oauth"`) |
|
||||
| **Paperless-ngx** | `paper.masu.rs` | Native (`django-allauth`) | NixOS env vars (`PAPERLESS_SOCIALACCOUNT_PROVIDERS`) |
|
||||
| **Mealie** | `cooking.masu.rs` | Native core feature | NixOS config (`services.mealie.settings` + `credentialsFile`) |
|
||||
| **Karakeep / Hoarder** | `keep.masu.rs` | Native (NextAuth OIDC) | NixOS env vars (`OAUTH_WELLKNOWN_URL`, etc.) |
|
||||
| **Audiobookshelf** | `read.masu.rs` | Native core feature (v2.3+) | Web UI (Settings → Authentication) |
|
||||
| **Actual Budget** | `money.masu.rs` | Native core feature (v24.3+) | NixOS config (`services.actual.settings.openId`) |
|
||||
|
||||
---
|
||||
|
||||
### Tier 2: OIDC via Plugins or Reverse Proxy Header Auth
|
||||
|
||||
These services do not have generic OIDC in their core web UI, but can support single sign-on through plugins or reverse proxy headers (`Remote-User` / `X-Forwarded-User`):
|
||||
|
||||
| Service | Hostname | Strategy | Notes |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Jellyfin** | `stream.masu.rs` | `jellyfin-plugin-sso` | Web clients work well; TV and native apps typically rely on Quick Connect. |
|
||||
| **Calibre-Web** | `books.masu.rs` | Reverse proxy header auth | Set `services.calibre-web.options.reverseProxyAuth.enable = true` behind an authenticating reverse proxy. |
|
||||
| **File Browser** | `files.masu.rs` | Reverse proxy header auth | Set `auth.method = "proxy"` and `auth.header = "X-Forwarded-User"` behind an authenticating reverse proxy. |
|
||||
| **Navidrome** | `music.masu.rs` | Reverse proxy header auth | Supports `ReverseProxyUserHeader` for web UI; Subsonic API clients (Feishin, etc.) still require native user passwords. |
|
||||
| **Stalwart** | `contacts.masu.rs` | OIDC Directory / SASL OAuth | Stalwart supports OIDC directories, but CardDAV/CalDAV clients usually require HTTP Basic Auth or application passwords. |
|
||||
|
||||
---
|
||||
|
||||
### Tier 3: Incompatible or No Native OIDC Support
|
||||
|
||||
- **Vaultwarden (`vault.masu.rs`):** Incompatible for vault decryption. Vaultwarden uses client-side zero-knowledge encryption where vault keys are derived from the user's master password. Bitwarden Enterprise SSO relies on a proprietary Key Connector that Vaultwarden does not implement.
|
||||
- **n8n (`n8n.masu.rs`):** SAML/OIDC SSO is an **Enterprise / commercial-only feature**; it is disabled in the free self-hosted Community edition.
|
||||
- **The Arr Stack (`download.masu.rs`):** Radarr, Sonarr, Lidarr, Readarr, Prowlarr, Bazarr, and Sabnzbd only support API keys, Basic Auth, or Forms.
|
||||
- **Transmission (`transmission.masu.rs`):** BitTorrent daemon; supports only HTTP Basic Auth / RPC whitelist.
|
||||
- **Uptime Kuma (`status.masu.rs`), ntfy (`ntfy.masu.rs`), The Lounge (`irc.masu.rs`), Pgweb (`pg.masu.rs`), Mathesar (`mathesar.masu.rs`), Hister (`hister.masu.rs`):** No native generic OIDC login mechanism.
|
||||
- **Infrastructure / Daemon Services:** PostgreSQL, InfluxDB, bind, avahi, cloudflared, wireguard, litestream (no user web UI).
|
||||
|
||||
---
|
||||
|
||||
## 2. General Setup Requirements
|
||||
|
||||
Configuring OIDC requires setup across four layers:
|
||||
|
||||
### A. Pocket ID Setup
|
||||
For each service, an OIDC client application must be registered in the Pocket ID admin interface:
|
||||
1. **Client ID:** A unique identifier slug (e.g. `gitea`, `immich`, `paperless`).
|
||||
2. **Client Secret:** A cryptographically secure random token.
|
||||
3. **Redirect URIs / Callback URLs:** The exact target URLs the service exposes for authorization code callbacks.
|
||||
4. **Scopes:** Usually `openid`, `profile`, and `email`.
|
||||
|
||||
### B. Secrets Management (agenix)
|
||||
All client secrets should be encrypted with `agenix` under the respective service directory:
|
||||
- Example: `platforms/nixos/modules/nmasur/presets/services/<service>/<service>-oidc.age`
|
||||
- Defined in NixOS under `config.secrets.<service>-oidc` with appropriate owner/group permissions.
|
||||
|
||||
### C. Network & Reverse Proxy (Caddy)
|
||||
1. **Back-Channel Connectivity:** When a user logs in, the service backend makes a server-to-server HTTPS call to `https://auth.masu.rs/api/oidc/token` to exchange the authorization code for tokens. Services hosted on `swan` (NAS) must be able to resolve and reach `auth.masu.rs` over HTTPS.
|
||||
2. **Proxy Headers:** Caddy's `reverse_proxy` handles `X-Forwarded-Proto`, `X-Forwarded-Host`, and `X-Forwarded-For` by default. Services should have reverse proxy trust enabled (e.g., `trusted_proxies = ["127.0.0.1"]`) so generated redirect URIs preserve the `https://` scheme.
|
||||
|
||||
---
|
||||
|
||||
## 3. Detailed Setup Requirements for Tier 1 Services
|
||||
|
||||
### 1. Immich (`photos.masu.rs`)
|
||||
- **Pocket ID Redirect URIs:**
|
||||
- Web: `https://photos.masu.rs/auth/login`
|
||||
- Mobile: `app.immich:///oauth-callback`
|
||||
- **Setup in `immich/immich.nix`:**
|
||||
```nix
|
||||
services.immich.settings.oauth = {
|
||||
enabled = true;
|
||||
issuerUrl = "https://${hostnames.auth}";
|
||||
clientId = "1f4e0f8d-6cee-4d67-8d53-74bf6c18ae09";
|
||||
clientSecret._secret = config.secrets.immich-oidc-secret.dest;
|
||||
scope = "openid profile email";
|
||||
autoRegister = true;
|
||||
buttonText = "Login with Pocket ID";
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Gitea (`git.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://git.masu.rs/user/oauth2/pocket-id/callback`
|
||||
- **Setup in Gitea:**
|
||||
Can be configured in the Web UI under **Site Administration → Authentication Sources** or via CLI:
|
||||
```bash
|
||||
gitea admin auth add-oauth \
|
||||
--name "Pocket ID" \
|
||||
--provider openidConnect \
|
||||
--key "<client_id>" \
|
||||
--secret "<client_secret>" \
|
||||
--auto-discover-url "https://auth.masu.rs/.well-known/openid-configuration"
|
||||
```
|
||||
- Optional: Enable automatic account linking by matching email.
|
||||
|
||||
### 3. Nextcloud (`cloud.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://cloud.masu.rs/apps/user_oidc/code`
|
||||
- **Setup in `nextcloud/nextcloud.nix`:**
|
||||
1. Add `user_oidc` to `services.nextcloud.extraApps`.
|
||||
2. Configure the provider via `nextcloud-occ`:
|
||||
```bash
|
||||
nextcloud-occ user_oidc:provider pocket-id \
|
||||
--clientid="<client_id>" \
|
||||
--clientsecret="<client_secret>" \
|
||||
--discoveryuri="https://auth.masu.rs/.well-known/openid-configuration" \
|
||||
--scope="openid profile email"
|
||||
```
|
||||
|
||||
### 4. Grafana (`metrics.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://metrics.masu.rs/login/generic_oauth`
|
||||
- **Setup in `grafana/grafana.nix`:**
|
||||
```nix
|
||||
services.grafana.settings = {
|
||||
auth.oauth_allow_insecure_email_lookup = true;
|
||||
"auth.generic_oauth" = {
|
||||
enabled = true;
|
||||
name = "Pocket ID";
|
||||
allow_sign_up = true;
|
||||
client_id = "85d879ed-1a86-4984-b33d-43806500ef98";
|
||||
client_secret = "$__file{${config.secrets.grafana-oidc-secret.dest}}";
|
||||
scopes = "openid profile email";
|
||||
auth_url = "https://${hostnames.auth}/authorize";
|
||||
token_url = "https://${hostnames.auth}/api/oidc/token";
|
||||
api_url = "https://${hostnames.auth}/api/oidc/userinfo";
|
||||
login_attribute_path = "preferred_username";
|
||||
skip_org_role_sync = true;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Paperless-ngx (`paper.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://paper.masu.rs/accounts/oidc/pocket-id/login/callback/`
|
||||
- **Setup in `paperless/paperless.nix`:**
|
||||
```nix
|
||||
services.paperless.settings = {
|
||||
PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";
|
||||
PAPERLESS_SOCIALACCOUNT_PROVIDERS = builtins.toJSON {
|
||||
openid_connect = {
|
||||
SERVERS = [
|
||||
{
|
||||
id = "pocket-id";
|
||||
name = "Pocket ID";
|
||||
server_url = "https://auth.masu.rs";
|
||||
token_auth_method = "client_secret_basic";
|
||||
APP = {
|
||||
client_id = "paperless";
|
||||
secret = "..."; # or via environmentFile
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
PAPERLESS_REDIRECT_LOGIN_TO_SSO = "true"; # Optional: bypass local login
|
||||
};
|
||||
```
|
||||
|
||||
### 6. Mealie (`cooking.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://cooking.masu.rs/login`
|
||||
- **Setup in `mealie/mealie.nix`:**
|
||||
```nix
|
||||
services.mealie = {
|
||||
credentialsFile = config.secrets.mealie-oidc-secret.dest;
|
||||
settings = {
|
||||
OIDC_AUTH_ENABLED = "true";
|
||||
OIDC_SIGNUP_ENABLED = "true";
|
||||
OIDC_CONFIGURATION_URL = "https://${hostnames.auth}/.well-known/openid-configuration";
|
||||
OIDC_CLIENT_ID = "040925ed-b39e-4442-b8e8-369c948c0cd2";
|
||||
OIDC_PROVIDER_NAME = "Pocket ID";
|
||||
OIDC_USER_CLAIM = "email";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### 7. Karakeep / Hoarder (`keep.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://keep.masu.rs/api/auth/callback/custom`
|
||||
- **Setup in `karakeep.nix`:**
|
||||
```nix
|
||||
services.karakeep.extraEnvironment = {
|
||||
OAUTH_WELLKNOWN_URL = "https://auth.masu.rs/.well-known/openid-configuration";
|
||||
OAUTH_CLIENT_ID = "hoarder";
|
||||
OAUTH_CLIENT_SECRET = "...";
|
||||
OAUTH_PROVIDER_NAME = "Pocket ID";
|
||||
OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING = "true";
|
||||
};
|
||||
```
|
||||
|
||||
### 8. Audiobookshelf (`read.masu.rs`)
|
||||
- **Pocket ID Redirect URIs:**
|
||||
- Web: `https://read.masu.rs/auth/openid/callback`
|
||||
- Mobile: `audiobookshelf://oauth`
|
||||
- **Setup in Audiobookshelf:**
|
||||
Configured in the Web UI (**Settings → Authentication → OpenID Connect**):
|
||||
- Issuer URL: `https://auth.masu.rs`
|
||||
- Client ID & Client Secret
|
||||
- Match user by email or username
|
||||
|
||||
### 9. Actual Budget (`money.masu.rs`)
|
||||
- **Pocket ID Redirect URI:** `https://money.masu.rs/openid/callback`
|
||||
- **Setup in `actualbudget/actualbudget.nix`:**
|
||||
```nix
|
||||
services.actual.settings = {
|
||||
loginMethod = "openid";
|
||||
openId = {
|
||||
discoveryURL = "https://${hostnames.auth}/.well-known/openid-configuration";
|
||||
client_id = "92afe9f8-7ef6-42ab-8a06-701df3c7179d";
|
||||
client_secret._secret = config.secrets.actualbudget-oidc-secret.dest;
|
||||
server_hostname = "https://${hostnames.budget}";
|
||||
authMethod = "openid";
|
||||
};
|
||||
};
|
||||
```
|
||||
*Note:* The optional end-to-end budget encryption password remains separate from the server authentication.
|
||||
|
||||
---
|
||||
|
||||
## 4. Forward Auth Architecture for Remaining Services
|
||||
|
||||
For services without native OIDC support (such as Calibre-Web, File Browser, Uptime Kuma, and the Arr stack), you can implement **Forward Auth via Caddy**:
|
||||
|
||||
1. Deploy an authenticating proxy (such as **OAuth2-Proxy** or **Authelia**) configured with Pocket ID as its OIDC provider.
|
||||
2. Configure Caddy routes using the `forward_auth` directive to verify user sessions with the proxy before forwarding requests to the target service.
|
||||
3. For services supporting reverse proxy authentication (Calibre-Web and File Browser), Caddy injects identity headers (e.g. `Remote-User: noah` or `X-Forwarded-User: noah`), enabling seamless single sign-on without requiring separate logins.
|
||||
Generated
+286
-134
@@ -1,12 +1,18 @@
|
||||
{
|
||||
"nodes": {
|
||||
"cl-nix-lite": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1728174978,
|
||||
"narHash": "sha256-Grqqg+xuicANB85j0gNEXxi9SBKY7bzGeTuyi95eGcY=",
|
||||
"lastModified": 1782994178,
|
||||
"narHash": "sha256-VGc3/2fe6hnOmNSLlOygEAbeS69v7A7rjKDKRzAgE2Q=",
|
||||
"owner": "hraban",
|
||||
"repo": "cl-nix-lite",
|
||||
"rev": "31cfe6275c341eb3120a99f4b1c8516c49a29d87",
|
||||
"rev": "eb584721e5e799bafe0c6210a8a1a398f90e8ac0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -22,11 +28,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755825449,
|
||||
"narHash": "sha256-XkiN4NM9Xdy59h69Pc+Vg4PxkSm9EWl6u7k6D5FZ5cM=",
|
||||
"lastModified": 1786845137,
|
||||
"narHash": "sha256-oQFip+v0luP8NIxJzmiW4Wu8bILsbFWom5l0zonl8hQ=",
|
||||
"owner": "lnl7",
|
||||
"repo": "nix-darwin",
|
||||
"rev": "8df64f819698c1fee0c2969696f54a843b2231e8",
|
||||
"rev": "4cff07de74b50e64bdd68cd4e722ab5b6b35ee48",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -43,11 +49,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756115622,
|
||||
"narHash": "sha256-iv8xVtmLMNLWFcDM/HcAPLRGONyTRpzL9NS09RnryRM=",
|
||||
"lastModified": 1781152676,
|
||||
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
|
||||
"owner": "nix-community",
|
||||
"repo": "disko",
|
||||
"rev": "bafad29f89e83b2d861b493aa23034ea16595560",
|
||||
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -59,11 +65,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1730663653,
|
||||
"narHash": "sha256-kFCUWettiFHDIqxCWWQ9qY8pVh+Lj+XL0Giyy/kdomg=",
|
||||
"lastModified": 1766808011,
|
||||
"narHash": "sha256-s83BS0abtIj7vzUf8JE0209KphfHA6qRw/92D9k/F+0=",
|
||||
"owner": "hraban",
|
||||
"repo": "flake-compat",
|
||||
"rev": "e5b16676185cb7548581c852f51ce7f3a49bba5e",
|
||||
"rev": "6e0aafdc4c4c2043c66f756d5045374b42184fc5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -76,11 +82,27 @@
|
||||
"flake-compat_2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat_3": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -90,6 +112,24 @@
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765835352,
|
||||
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"nur",
|
||||
@@ -132,7 +172,7 @@
|
||||
},
|
||||
"flake-utils_2": {
|
||||
"inputs": {
|
||||
"systems": "systems_2"
|
||||
"systems": "systems_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
@@ -148,27 +188,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"helix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756477005,
|
||||
"narHash": "sha256-e/FNNIT/LPuoruzRQZf5z5L5GApq1G2y2PShy+Lakhs=",
|
||||
"owner": "helix-editor",
|
||||
"repo": "helix",
|
||||
"rev": "77ff51caa440b1066e0a1920007ab5e148dd4856",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "helix-editor",
|
||||
"repo": "helix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -176,11 +195,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756496801,
|
||||
"narHash": "sha256-IYIsnPy+cJxe8RbDHBrCtfJY0ry2bG2H7WvMcewiGS8=",
|
||||
"lastModified": 1788487777,
|
||||
"narHash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=",
|
||||
"owner": "nix-community",
|
||||
"repo": "home-manager",
|
||||
"rev": "77a71380c38fb2a440b4b5881bbc839f6230e1cb",
|
||||
"rev": "693e8ce0fb240a73c116a03cfd7b19269c87af88",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -195,18 +214,16 @@
|
||||
"cl-nix-lite": "cl-nix-lite",
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"systems": "systems",
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
"nixpkgs": "nixpkgs_3",
|
||||
"systems": "systems_2",
|
||||
"treefmt-nix": "treefmt-nix_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756057867,
|
||||
"narHash": "sha256-ziR5eQGqRWhW8tf8r0TIplaqNt+HXu1G1X41LUr4IYo=",
|
||||
"lastModified": 1783182903,
|
||||
"narHash": "sha256-7IteXipJZfFepi5zakxe0jQ5i3vRBQguk0+Gtte1Fu4=",
|
||||
"owner": "hraban",
|
||||
"repo": "mac-app-util",
|
||||
"rev": "8414fa1e2cb775b17793104a9095aabeeada63ef",
|
||||
"rev": "039f33deef21782d4db97087f426504951239887",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -215,6 +232,46 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-caddy-withplugins": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"nixpkgs": "nixpkgs_5"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1784936738,
|
||||
"narHash": "sha256-X4w9BrFkisfzla3m1WwDJUicUbI7JRdhzq5aS7rrB0k=",
|
||||
"owner": "MichailiK",
|
||||
"repo": "nix-caddy-withplugins",
|
||||
"rev": "70307cfc568e5434cf42067d797b27c38ea69944",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "MichailiK",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nix-caddy-withplugins",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-index-database": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1788080100,
|
||||
"narHash": "sha256-n14luQo3F/ZLfCEHk1QieiI1nGnW92ZQZeZ0UIb0UMQ=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-index-database",
|
||||
"rev": "dbb978fa87faf13398e90ccbc52c343d21c7dd6d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-index-database",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix2vim": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils_2",
|
||||
@@ -223,11 +280,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755344765,
|
||||
"narHash": "sha256-k/Cvh/mzb5lSvilKdgwNBCyAyYmD8YPr1nc0sTSgwxI=",
|
||||
"lastModified": 1779563638,
|
||||
"narHash": "sha256-9CQKxDKDqVYdDhJW+A955AZwpwZjKUgvu0LbfsL6uyI=",
|
||||
"owner": "gytis-ivaskevicius",
|
||||
"repo": "nix2vim",
|
||||
"rev": "78467c8de07719f92397179844bf75cdf2e58b83",
|
||||
"rev": "6927414f0de10a18fb6bc9977717088777d50d0d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -259,11 +316,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751903740,
|
||||
"narHash": "sha256-PeSkNMvkpEvts+9DjFiop1iT2JuBpyknmBUs0Un0a4I=",
|
||||
"lastModified": 1769813415,
|
||||
"narHash": "sha256-nnVmNNKBi1YiBNPhKclNYDORoHkuKipoz7EtVnXO50A=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixos-generators",
|
||||
"rev": "032decf9db65efed428afd2fa39d80f7089085eb",
|
||||
"rev": "8946737ff703382fda7623b9fab071d037e897d5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -274,11 +331,58 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1754340878,
|
||||
"narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=",
|
||||
"lastModified": 1779796641,
|
||||
"narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "cab778239e705082fe97bb4990e0d24c50924c04",
|
||||
"rev": "25f538306313eae3927264466c70d7001dcea1df",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-25.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1765674936,
|
||||
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1788405554,
|
||||
"narHash": "sha256-r2f1oUwixlgq9zOdYLqJLfS/lWBT60/IITjhTKI59JU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a5cc6f2c37bf518436dc8d1c288ccd0c43c2f4c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-26.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1761236834,
|
||||
"narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d5faa84122bc0a1fd5d378492efce4e289f8eac1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -288,29 +392,61 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1735563628,
|
||||
"narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=",
|
||||
"owner": "nixos",
|
||||
"lastModified": 1782999065,
|
||||
"narHash": "sha256-5Dgj5+pIQYZKrXUGaLCk7CKfN3MmpwIhO94++WVxvng=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798",
|
||||
"rev": "80d591ed473cfc46329932c2aadac9b435342c7c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-24.05",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-26.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1756386758,
|
||||
"narHash": "sha256-1wxxznpW2CKvI9VdniaUnTT2Os6rdRJcRUf65ZK9OtE=",
|
||||
"lastModified": 1770107345,
|
||||
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dfb2f12e899db4876308eba6d93455ab7da304cd",
|
||||
"rev": "4533d9293756b63904b7238acb84ac8fe4c8c2c4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_5": {
|
||||
"locked": {
|
||||
"lastModified": 1781074563,
|
||||
"narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_6": {
|
||||
"locked": {
|
||||
"lastModified": 1788531059,
|
||||
"narHash": "sha256-hLD4l3QOGBQhkVp3mQ2lJ/YbEi99qUgKapb40KovZ88=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "801bef6abd86b91e51083066b83fb354a11fc640",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -320,13 +456,13 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"nixpkgs_7": {
|
||||
"locked": {
|
||||
"lastModified": 1728538411,
|
||||
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -338,17 +474,17 @@
|
||||
},
|
||||
"nur": {
|
||||
"inputs": {
|
||||
"flake-parts": "flake-parts",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1756524478,
|
||||
"narHash": "sha256-2oSBlcYCgwrVxUZwM8MV6hBFsfsWFbeN5ErQiCA+38s=",
|
||||
"lastModified": 1788594396,
|
||||
"narHash": "sha256-HLvxvUIMxBHl+vv/1OVTLPKrSLTinId97WBIxWWByJ0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nur",
|
||||
"rev": "e82a8b0095f54edb6bbbb1d862f3da502dca1396",
|
||||
"rev": "82ca52a282cd5f90afabcf7c9acd27571b253523",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -361,14 +497,16 @@
|
||||
"inputs": {
|
||||
"darwin": "darwin",
|
||||
"disko": "disko",
|
||||
"helix": "helix",
|
||||
"home-manager": "home-manager",
|
||||
"mac-app-util": "mac-app-util",
|
||||
"nix-caddy-withplugins": "nix-caddy-withplugins",
|
||||
"nix-index-database": "nix-index-database",
|
||||
"nix2vim": "nix2vim",
|
||||
"nixos-generators": "nixos-generators",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs": "nixpkgs_6",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nur": "nur",
|
||||
"rust-overlay": "rust-overlay",
|
||||
"wsl": "wsl",
|
||||
"zellij-switch": "zellij-switch",
|
||||
"zenyd-mpv-scripts": "zenyd-mpv-scripts"
|
||||
@@ -376,35 +514,14 @@
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"helix",
|
||||
"nixpkgs"
|
||||
]
|
||||
"nixpkgs": "nixpkgs_7"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1740623427,
|
||||
"narHash": "sha256-3SdPQrZoa4odlScFDUHd4CUPQ/R1gtH4Mq9u8CBiK8M=",
|
||||
"lastModified": 1788591095,
|
||||
"narHash": "sha256-Vh+BeLWfbTT9AecazIsQ/Tkg/RzJeX3lEduANf256WA=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "d342e8b5fd88421ff982f383c853f0fc78a847ab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"rust-overlay_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1736476219,
|
||||
"narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9",
|
||||
"rev": "c361047d3a538f547f1617bb6b410411929ac9cc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -414,21 +531,6 @@
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1689347925,
|
||||
"narHash": "sha256-ozenz5bFe1UUqOn7f60HRmgc01BgTGIKZ4Xl+HbocGQ=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-darwin",
|
||||
"rev": "2235d7e6cc29ae99878133c95e9fe5e157661ffb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-darwin",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
@@ -443,6 +545,21 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1689347925,
|
||||
"narHash": "sha256-ozenz5bFe1UUqOn7f60HRmgc01BgTGIKZ4Xl+HbocGQ=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-darwin",
|
||||
"rev": "2235d7e6cc29ae99878133c95e9fe5e157661ffb",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default-darwin",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_3": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
@@ -458,16 +575,49 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems_4": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755934250,
|
||||
"narHash": "sha256-CsDojnMgYsfshQw3t4zjRUkmMmUdZGthl16bXVWgRYU=",
|
||||
"lastModified": 1766000401,
|
||||
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "74e1a52d5bd9430312f8d1b8b0354c92c17453e5",
|
||||
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"treefmt-nix_2": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780220602,
|
||||
"narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "db947814a175b7ca6ded66e21383d938df01c227",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -478,17 +628,17 @@
|
||||
},
|
||||
"wsl": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat_2",
|
||||
"flake-compat": "flake-compat_3",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1755261305,
|
||||
"narHash": "sha256-EOqCupB5X5WoGVHVcfOZcqy0SbKWNuY3kq+lj1wHdu8=",
|
||||
"lastModified": 1784642409,
|
||||
"narHash": "sha256-hcbDqFuySAJawljt5r0sKBCJKYnbtGD0T/ZIozH1Dq0=",
|
||||
"owner": "nix-community",
|
||||
"repo": "NixOS-WSL",
|
||||
"rev": "203a7b463f307c60026136dd1191d9001c43457f",
|
||||
"rev": "eaeb18da90024448a60eb1ec7132eafa4003404e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -502,15 +652,17 @@
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"rust-overlay": "rust-overlay_2",
|
||||
"systems": "systems_3"
|
||||
"rust-overlay": [
|
||||
"rust-overlay"
|
||||
],
|
||||
"systems": "systems_4"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742588229,
|
||||
"narHash": "sha256-IPg0pBw0ciF+xl6viq3nK+dvZoDZrfBDui7dkPLz258=",
|
||||
"lastModified": 1781873766,
|
||||
"narHash": "sha256-V8baEgRBa2SqtCLbQ4EaphIMKsnAogY2oMdttWYhkEs=",
|
||||
"owner": "mostafaqanbaryan",
|
||||
"repo": "zellij-switch",
|
||||
"rev": "0e3c303c19890ccb03589230ac5a7c4307e573e4",
|
||||
"rev": "ced3c681afe40785b2853e3a976908466786ee79",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -522,11 +674,11 @@
|
||||
"zenyd-mpv-scripts": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1707704915,
|
||||
"narHash": "sha256-9P/8q/OZXfaJMS08acQP4h3/zUA5mKRQee0JmkXcz1w=",
|
||||
"lastModified": 1765050776,
|
||||
"narHash": "sha256-9gO+GkNoGsxAbMRrBWu0FfXEQtyTmHivlaxlYLpV2YM=",
|
||||
"owner": "zenyd",
|
||||
"repo": "mpv-scripts",
|
||||
"rev": "9bdce0050144cb24f92475f7bdd77180e0e4c26b",
|
||||
"rev": "62f4bb313c6cb6366672e78dea940e9da8fec84a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
# Used for specific stable packages
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-24.05";
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs/nixos-26.05";
|
||||
|
||||
# Used for MacOS system config
|
||||
darwin = {
|
||||
@@ -37,7 +37,13 @@
|
||||
# Better App install management in macOS
|
||||
mac-app-util = {
|
||||
url = "github:hraban/mac-app-util";
|
||||
inputs.nixpkgs.follows = "nixpkgs"; # Use system packages list for their inputs
|
||||
# inputs.nixpkgs.follows = "nixpkgs"; # Use system packages list for their inputs
|
||||
};
|
||||
|
||||
# Caddy with decoupled plugin hashes
|
||||
nix-caddy-withplugins = {
|
||||
url = "github:MichailiK/nix-caddy-withplugins/nixos-unstable";
|
||||
# Important: Do not follow nixpkgs here, per the README
|
||||
};
|
||||
|
||||
# Manage disk format and partitioning
|
||||
@@ -52,6 +58,11 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
nix-index-database = {
|
||||
url = "github:nix-community/nix-index-database";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# Convert Nix to Neovim config
|
||||
nix2vim = {
|
||||
url = "github:gytis-ivaskevicius/nix2vim";
|
||||
@@ -64,17 +75,23 @@
|
||||
flake = false;
|
||||
};
|
||||
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
};
|
||||
|
||||
# Zellij Switcher
|
||||
zellij-switch = {
|
||||
url = "github:mostafaqanbaryan/zellij-switch";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
inputs.rust-overlay.follows = "rust-overlay";
|
||||
};
|
||||
|
||||
# Text editor
|
||||
helix = {
|
||||
url = "github:helix-editor/helix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
# # Text editor
|
||||
# helix = {
|
||||
# url = "github:helix-editor/helix";
|
||||
# inputs.nixpkgs.follows = "nixpkgs";
|
||||
# inputs.rust-overlay.follows = "rust-overlay";
|
||||
# };
|
||||
|
||||
# # Nextcloud Apps
|
||||
# nextcloud-news = {
|
||||
@@ -111,6 +128,7 @@
|
||||
in
|
||||
{
|
||||
audiobooks = "read.${baseName}";
|
||||
auth = "auth.${baseName}";
|
||||
bookmarks = "keep.${baseName}";
|
||||
books = "books.${baseName}";
|
||||
budget = "money.${baseName}";
|
||||
@@ -119,6 +137,7 @@
|
||||
download = "download.${baseName}";
|
||||
files = "files.${baseName}";
|
||||
git = "git.${baseName}";
|
||||
hister = "hister.${baseName}";
|
||||
imap = "imap.purelymail.com";
|
||||
influxdb = "influxdb.${baseName}";
|
||||
irc = "irc.${baseName}";
|
||||
@@ -126,6 +145,7 @@
|
||||
mathesar = "mathesar.${baseName}";
|
||||
metrics = "metrics.${baseName}";
|
||||
minecraft = "minecraft.${baseName}";
|
||||
mumble = "mumble.${baseName}";
|
||||
n8n = "n8n.${baseName}";
|
||||
navidrome = "music.${baseName}";
|
||||
notifications = "ntfy.${baseName}";
|
||||
@@ -133,6 +153,7 @@
|
||||
photos = "photos.${baseName}";
|
||||
postgresql = "pg.${baseName}";
|
||||
prometheus = "prom.${baseName}";
|
||||
recipes = "cooking.${baseName}";
|
||||
secrets = "vault.${baseName}";
|
||||
smtp = "smtp.purelymail.com";
|
||||
status = "status.${baseName}";
|
||||
@@ -140,38 +161,9 @@
|
||||
transmission = "transmission.${baseName}";
|
||||
};
|
||||
|
||||
in
|
||||
rec {
|
||||
|
||||
lib = import ./lib inputs;
|
||||
flattenAttrset = attrs: builtins.foldl' lib.mergeAttrs { } (builtins.attrValues attrs);
|
||||
|
||||
nixosConfigurations = flattenAttrset (
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildNixos {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) lib.linuxHosts
|
||||
);
|
||||
|
||||
darwinConfigurations = flattenAttrset (
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildDarwin {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) lib.darwinHosts
|
||||
);
|
||||
|
||||
homeModules = builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
@@ -179,19 +171,6 @@
|
||||
) hosts
|
||||
) lib.hosts;
|
||||
|
||||
homeConfigurations = flattenAttrset (
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildHome {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) homeModules
|
||||
);
|
||||
|
||||
# Disk formatting, only used once
|
||||
diskoConfigurations = {
|
||||
root = import ./hosts/x86_64-linux/swan/root.nix;
|
||||
@@ -221,6 +200,51 @@
|
||||
) lib.linuxHosts # x86_64-linux = { arrow = ...; swan = ...; }
|
||||
;
|
||||
|
||||
in
|
||||
{
|
||||
|
||||
inherit lib;
|
||||
|
||||
nixosConfigurations = flattenAttrset (
|
||||
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildNixos {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) lib.linuxHosts
|
||||
);
|
||||
|
||||
darwinConfigurations = flattenAttrset (
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildDarwin {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) lib.darwinHosts
|
||||
);
|
||||
|
||||
homeConfigurations = flattenAttrset (
|
||||
builtins.mapAttrs (
|
||||
system: hosts:
|
||||
builtins.mapAttrs (
|
||||
name: module:
|
||||
lib.buildHome {
|
||||
inherit system module;
|
||||
specialArgs = { inherit hostnames; };
|
||||
}
|
||||
) hosts
|
||||
) homeModules
|
||||
);
|
||||
|
||||
# packages =
|
||||
# lib.forSystems lib.linuxSystems (
|
||||
# system: generateImagesForHosts system // lib.pkgsBySystem.${system}.nmasur
|
||||
@@ -280,7 +304,7 @@
|
||||
inherit (lib) overlays;
|
||||
};
|
||||
in
|
||||
pkgs.nixfmt-rfc-style
|
||||
pkgs.nixfmt
|
||||
);
|
||||
|
||||
# Templates for starting other projects quickly
|
||||
|
||||
@@ -12,9 +12,19 @@ rec {
|
||||
|
||||
nmasur.profiles = {
|
||||
base.enable = true;
|
||||
work.enable = true;
|
||||
extra.enable = true;
|
||||
gaming.enable = true;
|
||||
# work.enable = true;
|
||||
# extra.enable = true;
|
||||
# gaming.enable = true;
|
||||
};
|
||||
|
||||
# Corporate network runs a TLS-intercepting proxy. Trust its root CA so Nix
|
||||
# fetches don't fail with "self-signed certificate in certificate chain". The
|
||||
# cert lives outside this public repo at a root-owned, world-readable path so
|
||||
# the unprivileged Nix build user can read it (a copy under $HOME is not
|
||||
# traversable by nixbld). See docs/CHANGELOG.md to extract and install it.
|
||||
nmasur.presets.security.corporateCa = {
|
||||
enable = true;
|
||||
certFile = "/etc/ssl/corp-ca/CorpCA.pem";
|
||||
};
|
||||
|
||||
home-manager.users."Noah.Masur" = {
|
||||
@@ -26,10 +36,13 @@ rec {
|
||||
nmasur.profiles = {
|
||||
common.enable = true;
|
||||
darwin-base.enable = true;
|
||||
darwin-gaming.enable = true;
|
||||
llm-development.enable = true;
|
||||
power-user.enable = true;
|
||||
work.enable = true;
|
||||
experimental.enable = true;
|
||||
};
|
||||
nmasur.presets.services.mbsync.user = "noah";
|
||||
nmasur.presets.programs.git-work.work = {
|
||||
name = "Noah-Masur_1701";
|
||||
email = "${nmasur.settings.username}@take2games.com";
|
||||
|
||||
@@ -28,6 +28,7 @@ rec {
|
||||
nmasur.profiles = {
|
||||
common.enable = true;
|
||||
linux-base.enable = true;
|
||||
llm-development.enable = true;
|
||||
power-user.enable = true;
|
||||
};
|
||||
nmasur.presets.programs.helix.enable = true;
|
||||
|
||||
@@ -26,6 +26,7 @@ rec {
|
||||
nmasur.profiles = {
|
||||
common.enable = true;
|
||||
linux-base.enable = true;
|
||||
llm-development.enable = true;
|
||||
power-user.enable = true;
|
||||
};
|
||||
home.stateVersion = "23.05";
|
||||
@@ -33,6 +34,9 @@ rec {
|
||||
|
||||
system.stateVersion = "23.05";
|
||||
|
||||
# Temp: disable while this isn't working
|
||||
services.automatic-timezoned.enable = false;
|
||||
|
||||
# Not sure what's necessary but too afraid to remove anything
|
||||
boot.initrd.availableKernelModules = [
|
||||
"xhci_pci"
|
||||
|
||||
@@ -30,6 +30,7 @@ rec {
|
||||
linux-base.enable = true;
|
||||
linux-gui.enable = true;
|
||||
linux-gaming.enable = true;
|
||||
llm-development.enable = true;
|
||||
power-user.enable = true;
|
||||
developer.enable = true;
|
||||
experimental.enable = true;
|
||||
@@ -51,7 +52,7 @@ rec {
|
||||
|
||||
# Graphics and VMs
|
||||
boot.initrd.kernelModules = [ "amdgpu" ];
|
||||
boot.kernelModules = [ "kvm-amd" ];
|
||||
boot.kernelModules = [ "kvm-amd" "it87" ];
|
||||
services.xserver.videoDrivers = [ "amdgpu" ];
|
||||
|
||||
# Required binary blobs to boot on this machine
|
||||
@@ -68,16 +69,16 @@ rec {
|
||||
hardware.fancontrol.config = ''
|
||||
# Configuration file generated by pwmconfig, changes will be lost
|
||||
INTERVAL=10
|
||||
DEVPATH=hwmon0=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
|
||||
DEVNAME=hwmon0=amdgpu
|
||||
FCTEMPS=hwmon0/pwm1=hwmon0/temp1_input
|
||||
FCFANS= hwmon0/pwm1=hwmon0/fan1_input
|
||||
MINTEMP=hwmon0/pwm1=50
|
||||
MAXTEMP=hwmon0/pwm1=70
|
||||
MINSTART=hwmon0/pwm1=100
|
||||
MINSTOP=hwmon0/pwm1=10
|
||||
MINPWM=hwmon0/pwm1=10
|
||||
MAXPWM=hwmon0/pwm1=240
|
||||
DEVPATH=hwmon1=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
|
||||
DEVNAME=hwmon1=amdgpu
|
||||
FCTEMPS=hwmon1/pwm1=hwmon1/temp1_input
|
||||
FCFANS= hwmon1/pwm1=hwmon1/fan1_input
|
||||
MINTEMP=hwmon1/pwm1=50
|
||||
MAXTEMP=hwmon1/pwm1=70
|
||||
MINSTART=hwmon1/pwm1=100
|
||||
MINSTOP=hwmon1/pwm1=10
|
||||
MINPWM=hwmon1/pwm1=10
|
||||
MAXPWM=hwmon1/pwm1=240
|
||||
'';
|
||||
|
||||
# File systems must be declared in order to boot
|
||||
|
||||
+39
-19
@@ -66,14 +66,16 @@ lib
|
||||
overlays = [
|
||||
inputs.nur.overlays.default
|
||||
inputs.nix2vim.overlay
|
||||
inputs.zellij-switch.overlays.default
|
||||
inputs.helix.overlays.default
|
||||
] ++ (importOverlays ../overlays);
|
||||
(final: prev: {
|
||||
zellij-switch = inputs.zellij-switch.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||
})
|
||||
# inputs.helix.overlays.default
|
||||
]
|
||||
++ (importOverlays ../overlays);
|
||||
|
||||
# System types to support.
|
||||
supportedSystems = [
|
||||
"x86_64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-linux"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
@@ -97,7 +99,14 @@ lib
|
||||
system:
|
||||
import inputs.nixpkgs {
|
||||
inherit system overlays;
|
||||
config.permittedInsecurePackages = [ "litestream-0.3.13" ];
|
||||
config.permittedInsecurePackages = [
|
||||
"litestream-0.3.13"
|
||||
"electron-36.9.5"
|
||||
# Build-time-only dep of karakeep's frontend; CVEs don't reach
|
||||
# the runtime closure. Remove once nixpkgs bumps it.
|
||||
"pnpm-9.15.9"
|
||||
"keybase-gui-6.5.1"
|
||||
];
|
||||
config.allowUnfree = true;
|
||||
}
|
||||
);
|
||||
@@ -130,7 +139,8 @@ lib
|
||||
];
|
||||
extraSpecialArgs = {
|
||||
inherit colorscheme;
|
||||
} // specialArgs;
|
||||
}
|
||||
// specialArgs;
|
||||
};
|
||||
|
||||
buildNixos =
|
||||
@@ -144,16 +154,20 @@ lib
|
||||
pkgs = pkgsBySystem.${system};
|
||||
modules = [
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
inputs.nix-index-database.nixosModules.default
|
||||
inputs.disko.nixosModules.disko
|
||||
inputs.wsl.nixosModules.wsl
|
||||
{ imports = (nixFiles ../platforms/nixos); }
|
||||
module
|
||||
# (builtins.removeAttrs module [ "home-manager" ])
|
||||
{
|
||||
home-manager = {
|
||||
extraSpecialArgs = {
|
||||
inherit colorscheme;
|
||||
} // specialArgs;
|
||||
} // homeModule.home-manager;
|
||||
}
|
||||
// specialArgs;
|
||||
}
|
||||
// homeModule.home-manager;
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -168,19 +182,21 @@ lib
|
||||
inherit system specialArgs;
|
||||
modules = [
|
||||
inputs.home-manager.darwinModules.home-manager
|
||||
inputs.nix-index-database.darwinModules.nix-index
|
||||
inputs.mac-app-util.darwinModules.default
|
||||
{
|
||||
imports = (nixFiles ../platforms/nix-darwin);
|
||||
nixpkgs.pkgs = pkgsBySystem.${system};
|
||||
}
|
||||
module
|
||||
{
|
||||
home-manager = {
|
||||
extraSpecialArgs = {
|
||||
inherit colorscheme;
|
||||
} // specialArgs;
|
||||
} // homeModule.home-manager;
|
||||
}
|
||||
# Home Manager is intentionally NOT activated here. It is managed
|
||||
# standalone via `nh home switch` (see homeConfigurations, extracted
|
||||
# from the host module's `home-manager.users`). Strip that attr so
|
||||
# darwin-rebuild doesn't also activate a second, divergent HM
|
||||
# generation with different store paths (useUserPackages puts packages
|
||||
# in /etc/profiles/per-user vs. ~/.nix-profile standalone) -- that
|
||||
# mismatch broke the prompt after every darwin-rebuild until the next
|
||||
# `nh home switch`.
|
||||
(builtins.removeAttrs module [ "home-manager" ])
|
||||
];
|
||||
};
|
||||
|
||||
@@ -211,6 +227,7 @@ lib
|
||||
pkgs = pkgsBySystem.${system};
|
||||
modules = [
|
||||
inputs.home-manager.nixosModules.home-manager
|
||||
inputs.nix-index-database.nixosModules.default
|
||||
inputs.disko.nixosModules.disko
|
||||
inputs.wsl.nixosModules.wsl
|
||||
{
|
||||
@@ -222,12 +239,15 @@ lib
|
||||
home-manager = {
|
||||
extraSpecialArgs = {
|
||||
inherit colorscheme;
|
||||
} // specialArgs;
|
||||
} // homeModule.home-manager;
|
||||
}
|
||||
// specialArgs;
|
||||
}
|
||||
// homeModule.home-manager;
|
||||
}
|
||||
];
|
||||
specialArgs = {
|
||||
} // specialArgs;
|
||||
}
|
||||
// specialArgs;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
{
|
||||
|
||||
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
home.packages =
|
||||
let
|
||||
ldap_scheme = "ldaps";
|
||||
magic_prefix = "take";
|
||||
ldap_port = 3269;
|
||||
jq_parse = pkgs.writeShellScriptBin "ljq" ''
|
||||
jq --slurp \
|
||||
--raw-input 'split("\n\n")|map(split("\n")|map(select(.[0:1]!="#" and length>0)) |select(length > 0)|map(capture("^(?<key>[^:]*:?): *(?<value>.*)") |if .key[-1:.key|length] == ":" then .key=.key[0:-1]|.value=(.value|@base64d) else . end)| group_by(.key) | map({key:.[0].key,value:(if .|length > 1 then [.[].value] else .[].value end)}) | from_entries)' | jq -r 'del(.[].thumbnailPhoto)'
|
||||
'';
|
||||
ldap_script = pkgs.writeShellScriptBin "ldap" ''
|
||||
if ! [ "$LDAP_HOST" ]; then
|
||||
echo "No LDAP_HOST specified!"
|
||||
exit 1
|
||||
fi
|
||||
SEARCH_FILTER="$@"
|
||||
ldapsearch -LLL \
|
||||
-B -o ldif-wrap=no \
|
||||
-E pr=5000/prompt \
|
||||
-H "${ldap_scheme}://''${LDAP_HOST}:${builtins.toString ldap_port}" \
|
||||
-D "${pkgs.lib.toUpper magic_prefix}2\\${pkgs.lib.toLower config.user}" \
|
||||
-w "$(${pkgs._1password-cli}/bin/op item get T2 --fields label=password --reveal)" \
|
||||
-b "dc=''${LDAP_HOST//./,dc=}" \
|
||||
-s "sub" -x "(cn=''${SEARCH_FILTER})" \
|
||||
| ${jq_parse}/bin/ljq
|
||||
'';
|
||||
ldapm_script = pkgs.writeShellScriptBin "ldapm" ''
|
||||
if ! [ "$LDAP_HOST" ]; then
|
||||
echo "No LDAP_HOST specified!"
|
||||
exit 1
|
||||
fi
|
||||
${ldap_script}/bin/ldap "$@" | jq '[ .[].memberOf] | add'
|
||||
'';
|
||||
ldapg_script = pkgs.writeShellScriptBin "ldapg" ''
|
||||
if ! [ "$LDAP_HOST" ]; then
|
||||
echo "No LDAP_HOST specified!"
|
||||
exit 1
|
||||
fi
|
||||
${ldap_script}/bin/ldap "$@" | jq '[ .[].member] | add'
|
||||
'';
|
||||
ldapl_script = pkgs.writeShellScriptBin "ldapl" ''
|
||||
if ! [ "$LDAP_HOST" ]; then
|
||||
echo "No LDAP_HOST specified!"
|
||||
exit 1
|
||||
fi
|
||||
${ldap_script}/bin/ldap "*$@*" | jq -r '.[].name'
|
||||
'';
|
||||
in
|
||||
[
|
||||
ldap_script
|
||||
ldapm_script
|
||||
ldapg_script
|
||||
ldapl_script
|
||||
jq_parse
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
|
||||
# Homebrew - Mac-specific packages that aren't in Nix
|
||||
config = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
|
||||
# # Requires Homebrew to be installed
|
||||
system.activationScripts.preActivation.text = ''
|
||||
if ! xcode-select --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD xcode-select --install
|
||||
fi
|
||||
if ! /opt/homebrew/bin/brew --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
fi
|
||||
'';
|
||||
|
||||
# Add homebrew paths to CLI path
|
||||
home-manager.users.${config.user}.home.sessionPath = [
|
||||
"/opt/homebrew/bin/"
|
||||
"/opt/homebrew/opt/trash/bin/"
|
||||
];
|
||||
|
||||
homebrew = {
|
||||
enable = true;
|
||||
onActivation = {
|
||||
autoUpdate = false; # Don't update during rebuild
|
||||
cleanup = "zap"; # Uninstall all programs not declared
|
||||
upgrade = true;
|
||||
};
|
||||
global = {
|
||||
brewfile = true; # Run brew bundle from anywhere
|
||||
lockfiles = false; # Don't save lockfile (since running from anywhere)
|
||||
};
|
||||
brews = [
|
||||
"trash" # Delete files and folders to trash instead of rm
|
||||
];
|
||||
casks = [
|
||||
"1password" # 1Password will not launch from Nix on macOS
|
||||
# "gitify" # Git notifications in menu bar (downgrade manually from 4.6.1)
|
||||
"keybase" # GUI on Nix not available for macOS
|
||||
# "logitech-g-hub" # Mouse and keyboard management
|
||||
"logitune" # Logitech webcam firmware
|
||||
"meetingbar" # Show meetings in menu bar
|
||||
"scroll-reverser" # Different scroll style for mouse vs. trackpad
|
||||
"notunes" # Don't launch Apple Music with the play button
|
||||
"steam" # Not packaged for Nixon macOS
|
||||
"epic-games" # Not packaged for Nix
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
_inputs: _final: prev: {
|
||||
pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
|
||||
(_pyfinal: pyprev: {
|
||||
cheetah3 = pyprev.cheetah3.overridePythonAttrs (_old: {
|
||||
dontCheckPythonMetadata = true;
|
||||
});
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
inputs: final: prev: {
|
||||
|
||||
# Switch firefox to stable or unstable depending on need
|
||||
firefox-unwrapped = final.unstable.firefox-unwrapped;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
_inputs: _final: prev: {
|
||||
|
||||
kubernetes-helm = prev.kubernetes-helm.overrideAttrs (_old: {
|
||||
doCheck = false;
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
inputs: inputs.nix-caddy-withplugins.overlays.default
|
||||
@@ -0,0 +1,20 @@
|
||||
# Disable paho-mqtt's flaky test suite.
|
||||
#
|
||||
# paho-mqtt's checkPhase runs socket-based integration tests that hang in
|
||||
# the Nix sandbox (they eventually time out with a KeyboardInterrupt after
|
||||
# ~150s, reporting errors and failing the build). This breaks the flame
|
||||
# rebuild, where paho-mqtt is pulled in transitively (e.g. via mealie).
|
||||
#
|
||||
# Applied through pythonPackagesExtensions so it covers every Python
|
||||
# package set (python3Packages, python314Packages, ...).
|
||||
|
||||
_inputs: _final: prev: {
|
||||
pythonPackagesExtensions = prev.pythonPackagesExtensions ++ [
|
||||
(_pyfinal: pyprev: {
|
||||
paho-mqtt = pyprev.paho-mqtt.overridePythonAttrs (_old: {
|
||||
doCheck = false;
|
||||
doInstallCheck = false;
|
||||
});
|
||||
})
|
||||
];
|
||||
}
|
||||
@@ -21,6 +21,9 @@ let
|
||||
# [ package1.drv package2.drv ]
|
||||
(builtins.map (name: prev.callPackage name { }))
|
||||
|
||||
# Filter out packages that return null (e.g. platform-specific packages)
|
||||
(builtins.filter (v: v != null))
|
||||
|
||||
# Convert the list to an attrset
|
||||
# { package1 = package1.drv, package2 = package2.drv }
|
||||
listToAttrsByPnameOrName
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@
|
||||
inputs: _final: prev: {
|
||||
# Provides `pkgs.stable`.
|
||||
stable = import inputs.nixpkgs-stable {
|
||||
inherit (prev) system config;
|
||||
system = prev.stdenv.hostPlatform.system;
|
||||
config = { };
|
||||
overlays = [
|
||||
# inputs.self.overlays.vim-plugins
|
||||
];
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
inputs: _final: prev: {
|
||||
# Provides `pkgs.unstable`.
|
||||
unstable = import inputs.nixpkgs {
|
||||
inherit (prev) system config;
|
||||
system = prev.stdenv.hostPlatform.system;
|
||||
inherit (prev) config;
|
||||
overlays = [
|
||||
# inputs.self.overlays.vim-plugins
|
||||
];
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
lib,
|
||||
buildNpmPackage,
|
||||
fetchFromGitHub,
|
||||
nodejs,
|
||||
}:
|
||||
|
||||
buildNpmPackage rec {
|
||||
pname = "actualtap";
|
||||
version = "1.0.34";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "MattFaz";
|
||||
repo = "actualtap";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-I2yb3WCOXYx/6RXiKjePlpChrr9G1il44OdON/SeVCw=";
|
||||
};
|
||||
|
||||
npmDepsHash = "sha256-LlTLwUt0Yja4NSBLbaYPV/keawSJKffno/28rtSj/oQ=";
|
||||
|
||||
dontNpmBuild = true;
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace src/server.js \
|
||||
--replace-fail 'port: 3001' 'port: process.env.PORT || 3001'
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
mkdir -p $out/bin
|
||||
cat <<EOF > $out/bin/actualtap
|
||||
#!/bin/sh
|
||||
exec ${lib.getExe nodejs} $out/lib/node_modules/actualtap/src/server.js "\$@"
|
||||
EOF
|
||||
chmod +x $out/bin/actualtap
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "Automatically create transactions in Actual Budget when you use Tap-to-Pay on a mobile device";
|
||||
homepage = "https://github.com/MattFaz/actualtap";
|
||||
license = licenses.gpl3Only;
|
||||
maintainers = [ ];
|
||||
mainProgram = "actualtap";
|
||||
};
|
||||
}
|
||||
@@ -100,7 +100,7 @@
|
||||
lua.command = "${pkgs.stylua}/bin/stylua";
|
||||
black.command = "${pkgs.black}/bin/black";
|
||||
fish_indent.command = "${pkgs.fish}/bin/fish_indent";
|
||||
nixfmt.command = "${pkgs.nixfmt-rfc-style}/bin/nixfmt";
|
||||
nixfmt.command = "${pkgs.nixfmt}/bin/nixfmt";
|
||||
rustfmt.command = "${pkgs.rustfmt}/bin/rustfmt";
|
||||
shfmt = {
|
||||
command = "${pkgs.shfmt}/bin/shfmt";
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
plugins = [
|
||||
(pkgs.vimPlugins.nvim-treesitter.withPlugins (_plugins: [
|
||||
pkgs.nmasur.ini-grammar
|
||||
pkgs.nmasur.puppet-grammar
|
||||
pkgs.nmasur.rasi-grammar
|
||||
pkgs.nmasur.vimdoc-grammar
|
||||
pkgs.nmasur.tree-sitter-ini
|
||||
pkgs.nmasur.tree-sitter-puppet
|
||||
pkgs.nmasur.tree-sitter-rasi
|
||||
pkgs.nmasur.tree-sitter-vimdoc
|
||||
pkgs.tree-sitter-grammars.tree-sitter-bash
|
||||
pkgs.tree-sitter-grammars.tree-sitter-c
|
||||
pkgs.tree-sitter-grammars.tree-sitter-fish
|
||||
@@ -21,7 +21,6 @@
|
||||
pkgs.tree-sitter-grammars.tree-sitter-yaml
|
||||
]))
|
||||
pkgs.vimPlugins.vim-matchup # Better % jumping in languages
|
||||
pkgs.vimPlugins.playground # Tree-sitter experimenting
|
||||
pkgs.vimPlugins.nginx-vim
|
||||
pkgs.vimPlugins.vim-helm
|
||||
# pkgs.vimPlugins.hmts-nvim # Tree-sitter injections for home-manager
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Fix: Volnoti error: 'volnoti' has been removed due to lack of maintenance upstream.
|
||||
|
||||
{ pkgs, lib, ... }:
|
||||
if !pkgs.stdenv.hostPlatform.isLinux then
|
||||
null
|
||||
else
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "volnoti";
|
||||
version = "2013-09-23";
|
||||
@@ -27,7 +30,7 @@ pkgs.stdenv.mkDerivation {
|
||||
dbus
|
||||
gdk-pixbuf
|
||||
glib
|
||||
xorg.libX11
|
||||
libx11
|
||||
gtk2
|
||||
dbus-glib
|
||||
librsvg
|
||||
|
||||
+4
-15
@@ -2,21 +2,10 @@
|
||||
|
||||
{
|
||||
pkgs,
|
||||
fetchFromGitHub,
|
||||
...
|
||||
}:
|
||||
|
||||
# Maintain a static version so that the plugin hash doesn't keep breaking
|
||||
(pkgs.caddy.overrideAttrs rec {
|
||||
version = "2.10.2";
|
||||
src = fetchFromGitHub {
|
||||
owner = "caddyserver";
|
||||
repo = "caddy";
|
||||
tag = "v${version}";
|
||||
hash = "sha256-KvikafRYPFZ0xCXqDdji1rxlkThEDEOHycK8GP5e8vk=";
|
||||
};
|
||||
}).withPlugins
|
||||
{
|
||||
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.1" ];
|
||||
hash = "sha256-AcWko5513hO8I0lvbCLqVbM1eWegAhoM0J0qXoWL/vI=";
|
||||
}
|
||||
pkgs.caddy.withPlugins {
|
||||
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.1" ];
|
||||
hash = "sha256-HuVBmiT3kD6RrDejQ6SnjCN8f7pFdZlGtZFbEf47bks=";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
function exportHistory() {
|
||||
const now = new Date();
|
||||
const startTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0); // Beginning of today
|
||||
|
||||
browser.history.search({
|
||||
text: '',
|
||||
startTime: startTime,
|
||||
endTime: now,
|
||||
maxResults: 10000
|
||||
}).then(historyItems => {
|
||||
const historyData = JSON.stringify(historyItems, null, 2);
|
||||
const blob = new Blob([historyData], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const date = now.toISOString().slice(0, 10); // YYYY-MM-DD
|
||||
const filename = `firefox-history/history-${date}.json`;
|
||||
|
||||
browser.downloads.download({
|
||||
url: url,
|
||||
filename: filename,
|
||||
conflictAction: 'overwrite',
|
||||
saveAs: false
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
browser.alarms.create('daily-export', {
|
||||
periodInMinutes: 60 // every 1 hour
|
||||
});
|
||||
|
||||
browser.alarms.onAlarm.addListener(alarm => {
|
||||
if (alarm.name === 'daily-export') {
|
||||
exportHistory();
|
||||
}
|
||||
});
|
||||
|
||||
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.command === "exportHistory") {
|
||||
exportHistory();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "History Exporter",
|
||||
"version": "1.1",
|
||||
"description": "Automatically exports today's browsing history.",
|
||||
"permissions": [
|
||||
"history",
|
||||
"downloads",
|
||||
"alarms"
|
||||
],
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "[email protected]",
|
||||
"data_collection_permissions": {
|
||||
"required": ["none"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "firefox-history-exporter";
|
||||
version = "1.1";
|
||||
src = ./.;
|
||||
|
||||
nativeBuildInputs = [ pkgs.zip ];
|
||||
|
||||
dontUnpack = true;
|
||||
|
||||
installPhase = ''
|
||||
dst="$out/share/mozilla/extensions/{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
|
||||
mkdir -p "$dst"
|
||||
zip -j "$dst/firefox-history-exporter@nmasur.com.xpi" \
|
||||
"${src}/manifest.json" \
|
||||
"${src}/background.js" \
|
||||
"${src}/popup.html" \
|
||||
"${src}/popup.js"
|
||||
'';
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
description = "Automatically exports today's browsing history.";
|
||||
license = licenses.mit;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>History Exporter</title>
|
||||
<style>
|
||||
body {
|
||||
width: 200px;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
button {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>History Exporter</h1>
|
||||
<button id="export-button">Export Now</button>
|
||||
<p id="status"></p>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
document.getElementById('export-button').addEventListener('click', () => {
|
||||
browser.runtime.sendMessage({command: "exportHistory"});
|
||||
|
||||
const statusElement = document.getElementById('status');
|
||||
statusElement.textContent = 'Exporting...';
|
||||
setTimeout(() => {
|
||||
statusElement.textContent = 'Export complete!';
|
||||
}, 2000);
|
||||
});
|
||||
@@ -89,6 +89,13 @@ let
|
||||
drf-access-policy
|
||||
# psycopg-binary
|
||||
;
|
||||
# clevercsv's test_encoding_chardet is nondeterministic (chardet
|
||||
# returns None instead of ISO-8859-1/KOI8-R on some platforms),
|
||||
# breaking the build. Skip its test suite.
|
||||
clevercsv = pyprev.clevercsv.overridePythonAttrs (_old: {
|
||||
doCheck = false;
|
||||
doInstallCheck = false;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
lib,
|
||||
fetchFromGitHub,
|
||||
nodejs_20,
|
||||
nodejs_22,
|
||||
buildNpmPackage,
|
||||
nodePackages,
|
||||
typescript,
|
||||
node-gyp,
|
||||
python3,
|
||||
gcc,
|
||||
gnumake,
|
||||
@@ -29,10 +30,10 @@ buildNpmPackage (finalAttrs: rec {
|
||||
npmDepsHash = "sha256-N8xqRYFelolNGTEhG22M7KJ7B5U/uW7o+/XfLF8rHMg=";
|
||||
|
||||
nativeBuildInputs = [
|
||||
nodejs_20
|
||||
nodePackages.typescript
|
||||
nodejs_22
|
||||
typescript
|
||||
python3
|
||||
nodePackages.node-gyp
|
||||
node-gyp
|
||||
gcc
|
||||
gnumake
|
||||
];
|
||||
@@ -53,8 +54,8 @@ buildNpmPackage (finalAttrs: rec {
|
||||
# export npm_config_build_from_source=true
|
||||
# export npm_config_unsafe_perm=true
|
||||
# export BINARY_SITE=none
|
||||
# export PATH=${nodePackages.node-gyp}/bin:$PATH
|
||||
# export npm_config_node_gyp=${nodePackages.node-gyp}/bin/node-gyp
|
||||
# export PATH=${node-gyp}/bin:$PATH
|
||||
# export npm_config_node_gyp=${node-gyp}/bin/node-gyp
|
||||
|
||||
# npm rebuild better-sqlite3 --build-from-source --verbose
|
||||
|
||||
@@ -64,7 +65,7 @@ buildNpmPackage (finalAttrs: rec {
|
||||
installPhase = ''
|
||||
mkdir -p $out/{bin,lib}
|
||||
cp -r . $out/lib/prometheus-actual-exporter
|
||||
makeWrapper ${lib.getExe nodejs_20} $out/bin/prometheus-actual-exporter \
|
||||
makeWrapper ${lib.getExe nodejs_22} $out/bin/prometheus-actual-exporter \
|
||||
--add-flags "$out/lib/prometheus-actual-exporter/dist/app.js"
|
||||
'';
|
||||
|
||||
@@ -74,8 +75,8 @@ buildNpmPackage (finalAttrs: rec {
|
||||
export npm_config_build_from_source=true
|
||||
export npm_config_unsafe_perm=true
|
||||
export BINARY_SITE=none
|
||||
export PATH=${nodePackages.node-gyp}/bin:$PATH
|
||||
export npm_config_node_gyp=${nodePackages.node-gyp}/bin/node-gyp
|
||||
export PATH=${node-gyp}/bin:$PATH
|
||||
export npm_config_node_gyp=${node-gyp}/bin/node-gyp
|
||||
|
||||
sed -i '/"install"/d' node_modules/better-sqlite3/package.json
|
||||
rm -f node_modules/better-sqlite3/build/Release/better_sqlite3.node || true
|
||||
|
||||
@@ -1,39 +1,42 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
buildDotnetModule,
|
||||
fetchFromGitHub,
|
||||
dotnetCorePackages,
|
||||
}:
|
||||
|
||||
buildDotnetModule rec {
|
||||
pname = "slsk-batchdl";
|
||||
version = "2.4.7";
|
||||
if !stdenv.hostPlatform.isLinux then
|
||||
null
|
||||
else
|
||||
buildDotnetModule rec {
|
||||
pname = "slsk-batchdl";
|
||||
version = "2.4.7";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "fiso64";
|
||||
repo = "slsk-batchdl";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-P7V7YJUA1bkfp13Glb1Q+NJ7iTya/xgO1TM88z1Nddc=";
|
||||
};
|
||||
src = fetchFromGitHub {
|
||||
owner = "fiso64";
|
||||
repo = "slsk-batchdl";
|
||||
rev = "v${version}";
|
||||
sha256 = "sha256-P7V7YJUA1bkfp13Glb1Q+NJ7iTya/xgO1TM88z1Nddc=";
|
||||
};
|
||||
|
||||
projectFile = "slsk-batchdl/slsk-batchdl.csproj";
|
||||
nugetDeps = ./nuget-deps.nix;
|
||||
projectFile = "slsk-batchdl/slsk-batchdl.csproj";
|
||||
nugetDeps = ./nuget-deps.nix;
|
||||
|
||||
dotnet-sdk = dotnetCorePackages.sdk_8_0;
|
||||
dotnet-runtime = dotnetCorePackages.runtime_8_0;
|
||||
dotnet-sdk = dotnetCorePackages.sdk_8_0;
|
||||
dotnet-runtime = dotnetCorePackages.runtime_8_0;
|
||||
|
||||
# Patch the project file to use .NET 8
|
||||
postPatch = ''
|
||||
substituteInPlace slsk-batchdl/slsk-batchdl.csproj \
|
||||
--replace-fail "net6.0" "net8.0"
|
||||
'';
|
||||
postPatch = ''
|
||||
substituteInPlace slsk-batchdl/slsk-batchdl.csproj \
|
||||
--replace-fail "net6.0" "net8.0"
|
||||
'';
|
||||
|
||||
doCheck = false;
|
||||
doCheck = false;
|
||||
|
||||
meta = with lib; {
|
||||
description = "A batch downloader for Soulseek";
|
||||
homepage = "https://github.com/fiso64/slsk-batchdl";
|
||||
platforms = platforms.linux;
|
||||
mainProgram = "slsk-batchdl";
|
||||
};
|
||||
}
|
||||
meta = with lib; {
|
||||
description = "A batch downloader for Soulseek";
|
||||
homepage = "https://github.com/fiso64/slsk-batchdl";
|
||||
platforms = platforms.linux;
|
||||
mainProgram = "slsk-batchdl";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
# Specify AWS_PROFILE and AWS_REGION before running this script
|
||||
|
||||
aws ec2 describe-instances \
|
||||
--filters "Name=instance-state-name,Values=running" |
|
||||
jq -r \
|
||||
'.Reservations[]
|
||||
--filters "Name=instance-state-name,Values=running" |
|
||||
jq -r \
|
||||
'.Reservations[]
|
||||
| .Instances[]
|
||||
| .InstanceId + " - " +
|
||||
(.PrivateIpAddress // "n/a") + " - " +
|
||||
(.PublicIpAddress // "n/a") + " - " +
|
||||
(.Tags // [] | from_entries | .Name // "n/a")' |
|
||||
fzf \
|
||||
--height 100% \
|
||||
--layout reverse \
|
||||
--header $'Press Enter to start SSM session\nInstance ID - Private IP - Public IP - Name' \
|
||||
--preview "aws ec2 describe-instances --instance-ids \"\$(echo {} | cut -d' ' -f1)\" | jq -r '.Reservations[].Instances[0]'" \
|
||||
--bind "enter:become(aws ssm start-session --target \$(echo {} | cut -d' ' -f1))"
|
||||
fzf \
|
||||
--height 100% \
|
||||
--layout reverse \
|
||||
--header $'Press Enter to start SSM session\nInstance ID - Private IP - Public IP - Name' \
|
||||
--preview "aws ec2 describe-instances --instance-ids \"\$(echo {} | cut -d' ' -f1)\" | jq -r '.Reservations[].Instances[0]'" \
|
||||
--bind "enter:become(aws ssm start-session --document-name 'AWS-StartInteractiveCommand' --parameters '{\"command\": [\"bash\"]}' --target \$(echo {} | cut -d' ' -f1))"
|
||||
|
||||
@@ -5,7 +5,7 @@ pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
git
|
||||
stylua
|
||||
nixfmt-rfc-style
|
||||
nixfmt
|
||||
shfmt
|
||||
shellcheck
|
||||
];
|
||||
|
||||
@@ -1,44 +1,47 @@
|
||||
{ pkgs, ... }:
|
||||
{ pkgs, lib, ... }:
|
||||
|
||||
# Inspired by https://github.com/cleverca22/nix-tests/blob/master/kexec/justdoit.nix
|
||||
# This script will partition and format drives; use at your own risk!
|
||||
|
||||
pkgs.writeShellScriptBin "installer" ''
|
||||
set -e
|
||||
if !pkgs.stdenv.hostPlatform.isLinux then
|
||||
null
|
||||
else
|
||||
pkgs.writeShellScriptBin "installer" ''
|
||||
set -e
|
||||
|
||||
DISK=$1
|
||||
FLAKE=$2
|
||||
PARTITION_PREFIX=""
|
||||
DISK=$1
|
||||
FLAKE=$2
|
||||
PARTITION_PREFIX=""
|
||||
|
||||
if [ -z "$DISK" ] || [ -z "$FLAKE" ]; then
|
||||
${pkgs.gum}/bin/gum style --width 50 --margin "1 2" --padding "2 4" \
|
||||
--foreground "#fb4934" \
|
||||
"Missing required parameter." \
|
||||
"Usage: installer -- <disk> <host>" \
|
||||
"Example: installer -- nvme0n1 tempest" \
|
||||
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 tempest"
|
||||
echo "(exiting)"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$DISK" ] || [ -z "$FLAKE" ]; then
|
||||
${pkgs.gum}/bin/gum style --width 50 --margin "1 2" --padding "2 4" \
|
||||
--foreground "#fb4934" \
|
||||
"Missing required parameter." \
|
||||
"Usage: installer -- <disk> <host>" \
|
||||
"Example: installer -- nvme0n1 tempest" \
|
||||
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 tempest"
|
||||
echo "(exiting)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$DISK" in nvme*)
|
||||
PARTITION_PREFIX="p"
|
||||
esac
|
||||
case "$DISK" in nvme*)
|
||||
PARTITION_PREFIX="p"
|
||||
esac
|
||||
|
||||
${pkgs.gum}/bin/gum confirm \
|
||||
"This will ERASE ALL DATA on the disk /dev/''${DISK}. Are you sure you want to continue?" \
|
||||
--default=false
|
||||
${pkgs.gum}/bin/gum confirm \
|
||||
"This will ERASE ALL DATA on the disk /dev/''${DISK}. Are you sure you want to continue?" \
|
||||
--default=false
|
||||
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mklabel gpt
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart primary 512MiB 100%
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart ESP fat32 1MiB 512MiB
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- set 3 esp on
|
||||
mkfs.ext4 -L nixos /dev/''${DISK}''${PARTITION_PREFIX}1
|
||||
mkfs.fat -F 32 -n boot /dev/''${DISK}''${PARTITION_PREFIX}2
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mklabel gpt
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart primary 512MiB 100%
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart ESP fat32 1MiB 512MiB
|
||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- set 3 esp on
|
||||
mkfs.ext4 -L nixos /dev/''${DISK}''${PARTITION_PREFIX}1
|
||||
mkfs.fat -F 32 -n boot /dev/''${DISK}''${PARTITION_PREFIX}2
|
||||
|
||||
mount /dev/disk/by-label/nixos /mnt
|
||||
mkdir --parents /mnt/boot
|
||||
mount /dev/disk/by-label/boot /mnt/boot
|
||||
mount /dev/disk/by-label/nixos /mnt
|
||||
mkdir --parents /mnt/boot
|
||||
mount /dev/disk/by-label/boot /mnt/boot
|
||||
|
||||
${pkgs.nixos-install-tools}/bin/nixos-install --flake github:nmasur/dotfiles#''${FLAKE}
|
||||
''
|
||||
${pkgs.nixos-install-tools}/bin/nixos-install --flake github:nmasur/dotfiles#''${FLAKE}
|
||||
''
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{ pkgs, ... }:
|
||||
|
||||
pkgs.writeShellScriptBin "rebuild" ''
|
||||
echo ${pkgs.system}
|
||||
SYSTEM=${if pkgs.stdenv.isDarwin then "darwin" else "linux"}
|
||||
echo ${pkgs.stdenv.hostPlatform.system}
|
||||
SYSTEM=${if pkgs.stdenv.hostPlatform.isDarwin then "darwin" else "linux"}
|
||||
if [ "$SYSTEM" == "darwin" ]; then
|
||||
sudo darwin-rebuild switch --flake ${builtins.toString ../../../../.}
|
||||
else
|
||||
|
||||
@@ -17,23 +17,27 @@ in
|
||||
home.packages = [
|
||||
pkgs.nerd-fonts.victor-mono # Used for Vim and Terminal
|
||||
pkgs.nerd-fonts.hack # For Polybar, Rofi
|
||||
|
||||
# Maple Mono NF (Ligature unhinted)
|
||||
pkgs.maple-mono.NF-unhinted
|
||||
];
|
||||
fonts.fontconfig = {
|
||||
enable = true;
|
||||
defaultFonts.monospace = [ "Victor Mono" ];
|
||||
defaultFonts.monospace = [ "Maple Mono NF" ];
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.fonts = {
|
||||
names = [ "pango:Victor Mono" ];
|
||||
# names = [ "pango:Victor Mono" ];
|
||||
names = [ "pango:Maple Mono" ];
|
||||
# style = "Regular";
|
||||
# size = 11.0;
|
||||
};
|
||||
services.polybar.config."bar/main".font-0 = "Hack Nerd Font:size=10;2";
|
||||
programs.rofi.font = "Hack Nerd Font 14";
|
||||
programs.alacritty.settings.font.normal.family = "VictorMono";
|
||||
programs.kitty.font.name = "VictorMono Nerd Font Mono";
|
||||
nmasur.presets.programs.wezterm.font = "VictorMono Nerd Font Mono";
|
||||
programs.ghostty.settings.font-family = "VictorMono Nerd Font Mono";
|
||||
programs.alacritty.settings.font.normal.family = "Maple Mono NF";
|
||||
programs.kitty.font.name = "Maple Mono NF";
|
||||
nmasur.presets.programs.wezterm.font = "Maple Mono NF";
|
||||
programs.ghostty.settings.font-family = "Maple Mono NF";
|
||||
services.dunst.settings.global.font = "Hack Nerd Font 14";
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Cursor
|
||||
home.pointerCursor = {
|
||||
enable = true;
|
||||
name = "Adwaita";
|
||||
package = pkgs.adwaita-icon-theme;
|
||||
size = 24;
|
||||
@@ -48,6 +49,7 @@ in
|
||||
gtk4.extraConfig = {
|
||||
gtk-application-prefer-dark-theme = config.theme.mode == "dark";
|
||||
};
|
||||
gtk4.theme = null;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -18,16 +18,17 @@ in
|
||||
"1password"
|
||||
"_1password-gui"
|
||||
"1password-cli"
|
||||
"onepassword-password-manager" # Firefox extension
|
||||
# "onepassword-password-manager" # Firefox extension
|
||||
];
|
||||
home.packages = [
|
||||
pkgs._1password-cli
|
||||
] ++ (if pkgs.stdenv.isLinux then [ pkgs._1password-gui ] else [ ]);
|
||||
]
|
||||
++ (if pkgs.stdenv.hostPlatform.isLinux then [ pkgs._1password-gui ] else [ ]);
|
||||
|
||||
# Firefox extension
|
||||
programs.firefox.profiles.default.extensions.packages = [
|
||||
pkgs.nur.repos.rycee.firefox-addons.onepassword-password-manager
|
||||
];
|
||||
# # Firefox extension
|
||||
# programs.firefox.profiles.default.extensions.packages = [
|
||||
# pkgs.nur.repos.rycee.firefox-addons.onepassword-password-manager
|
||||
# ];
|
||||
};
|
||||
|
||||
# # https://1password.community/discussion/135462/firefox-extension-does-not-connect-to-linux-app
|
||||
|
||||
@@ -174,8 +174,10 @@ in
|
||||
filters = {
|
||||
"text/plain" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
|
||||
"text/calendar" = "${pkgs.gawk}/bin/awk -f ${pkgs.aerc}/libexec/aerc/filters/calendar";
|
||||
# "text/html" =
|
||||
# "${pkgs.aerc}/libexec/aerc/filters/html | ${pkgs.aerc}/libexec/aerc/filters/colorize"; # Requires w3m, dante
|
||||
"text/html" =
|
||||
"${pkgs.aerc}/libexec/aerc/filters/html | ${pkgs.aerc}/libexec/aerc/filters/colorize"; # Requires w3m, dante
|
||||
"!${pkgs.chawan}/bin/cha --type text/html --opt display.image-mode=kitty --opt display.columns=100 --opt display.force-columns=true";
|
||||
# "text/*" =
|
||||
# ''${pkgs.bat}/bin/bat -fP --file-name="$AERC_FILENAME "'';
|
||||
"message/delivery-status" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
|
||||
@@ -200,11 +202,14 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
xdg.desktopEntries.aerc = lib.mkIf (pkgs.stdenv.isLinux) {
|
||||
# Used for macOS
|
||||
xdg.enable = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin true;
|
||||
|
||||
xdg.desktopEntries.aerc = lib.mkIf (pkgs.stdenv.hostPlatform.isLinux) {
|
||||
name = "aerc";
|
||||
exec = "${lib.getExe config.nmasur.presets.services.i3.terminal} aerc %u";
|
||||
exec = "${lib.getExe config.nmasur.presets.services.i3.terminal} -e aerc %u";
|
||||
};
|
||||
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.isLinux {
|
||||
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
|
||||
"${config.xsession.windowManager.i3.config.modifier}+Shift+e" =
|
||||
let
|
||||
terminal = config.nmasur.presets.services.i3.terminal;
|
||||
@@ -212,7 +217,7 @@ in
|
||||
if terminal == pkgs.wezterm then
|
||||
"start --class com.noah.aerc -- aerc"
|
||||
else
|
||||
"--class=com.noah.aerc --command=aerc";
|
||||
"--class=com.noah.aerc -e aerc";
|
||||
in
|
||||
"exec ${
|
||||
# Don't name the script `aerc` or it will affect grep
|
||||
|
||||
@@ -32,7 +32,6 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
accounts.calendar.accounts.default = {
|
||||
basePath = "other/calendars"; # Where to save calendars in ~ directory
|
||||
name = "personal";
|
||||
local.type = "filesystem";
|
||||
primary = true;
|
||||
|
||||
@@ -14,9 +14,14 @@ in
|
||||
options.nmasur.presets.programs.calibre.enable = lib.mkEnableOption "Calibre e-book manager";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
home.packages = [ pkgs.calibre ];
|
||||
home.packages = lib.mkIf pkgs.stdenv.hostPlatform.isLinux [ pkgs.calibre ];
|
||||
home.sessionVariables = {
|
||||
CALIBRE_USE_DARK_PALETTE = 1;
|
||||
};
|
||||
|
||||
home.file.".Brewfile".text = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin /* homebrew */ ''
|
||||
cask "calibre" # Nix package broken on macOS
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -18,15 +18,16 @@ in
|
||||
ca = "cargo";
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
gcc
|
||||
rustc
|
||||
cargo
|
||||
cargo-watch
|
||||
clippy
|
||||
rustfmt
|
||||
pkg-config
|
||||
openssl
|
||||
home.packages = [
|
||||
pkgs.gcc
|
||||
pkgs.rustc
|
||||
pkgs.cargo
|
||||
pkgs.stable.cargo-watch
|
||||
pkgs.clippy
|
||||
pkgs.rustfmt
|
||||
pkgs.pkg-config
|
||||
pkgs.openssl
|
||||
pkgs.rust-analyzer
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs.chawan;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.programs.chawan.enable = lib.mkEnableOption "chawan TUI web browser";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
programs.chawan = {
|
||||
enable = true;
|
||||
settings = {
|
||||
external.copy-cmd = if pkgs.stdenv.hostPlatform.isLinux then "xclip -selection clipboard -in" else "pbcopy";
|
||||
};
|
||||
};
|
||||
|
||||
# Set Chawan as the default app for manual pages
|
||||
home.sessionVariables = {
|
||||
MANPAGER = "${lib.getExe pkgs.chawan} -T text/x-ansi";
|
||||
};
|
||||
|
||||
programs.fish.shellAbbrs.man = "mancha";
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
# Always make the dotfiles directory considered safe for git and direnv
|
||||
programs.git.extraConfig.safe.directory = cfg.path;
|
||||
programs.git.settings.safe.directory = cfg.path;
|
||||
programs.direnv.config.whitelist.prefix = [ cfg.path ];
|
||||
|
||||
home.activation = {
|
||||
|
||||
@@ -24,6 +24,8 @@ in
|
||||
programs.firefox = {
|
||||
enable = true;
|
||||
package = pkgs.firefox;
|
||||
# Use appropriate default path depending on the OS
|
||||
configPath = if pkgs.stdenv.hostPlatform.isDarwin then "Library/Application Support/Firefox" else ".mozilla/firefox";
|
||||
profiles.default = {
|
||||
id = 0;
|
||||
name = "default";
|
||||
@@ -46,6 +48,8 @@ in
|
||||
ublacklist
|
||||
vimium
|
||||
wappalyzer # TODO: only for work profile
|
||||
# pkgs.nmasur.firefox-history-exporter
|
||||
# copy-as-markdown
|
||||
# saml-tracer
|
||||
# text-fragment
|
||||
];
|
||||
@@ -53,7 +57,7 @@ in
|
||||
"app.update.auto" = false;
|
||||
"browser.aboutConfig.showWarning" = false;
|
||||
"browser.warnOnQuit" = false;
|
||||
"browser.quitShortcut.disabled" = if pkgs.stdenv.isLinux then true else false;
|
||||
"browser.quitShortcut.disabled" = if pkgs.stdenv.hostPlatform.isLinux then true else false;
|
||||
"browser.theme.dark-private-windows" = true;
|
||||
"browser.toolbars.bookmarks.visibility" = false;
|
||||
"browser.startup.page" = 3; # Restore previous session
|
||||
@@ -61,6 +65,7 @@ in
|
||||
"trailhead.firstrun.didSeeAboutWelcome" = true; # Disable welcome splash
|
||||
"dom.forms.autocomplete.formautofill" = false; # Disable autofill
|
||||
"extensions.formautofill.creditCards.enabled" = false; # Disable credit cards
|
||||
"extensions.autoDisableScopes" = false; # Enable extensions automatically
|
||||
"dom.payments.defaults.saveAddress" = false; # Disable address save
|
||||
"general.autoScroll" = true; # Drag middle-mouse to scroll
|
||||
"services.sync.prefs.sync.general.autoScroll" = false; # Prevent disabling autoscroll
|
||||
@@ -184,10 +189,10 @@ in
|
||||
|
||||
};
|
||||
|
||||
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.isLinux {
|
||||
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
|
||||
"${config.xsession.windowManager.i3.config.modifier}+Shift+b" = "exec ${
|
||||
# Don't name the script `firefox` or it will affect grep
|
||||
builtins.toString (
|
||||
toString (
|
||||
pkgs.writeShellScript "focus-ff.sh" ''
|
||||
count=$(ps aux | grep -c firefox)
|
||||
if [ "$count" -eq 1 ]; then
|
||||
|
||||
@@ -13,13 +13,35 @@ in
|
||||
|
||||
options.nmasur.presets.programs.fish-darwin.enable = lib.mkEnableOption {
|
||||
description = "Fish macOS options";
|
||||
default = config.nmasur.presets.programs.fish && pkgs.stdenv.isDarwin;
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.fish.shellAbbrs = {
|
||||
# Shortcut to edit hosts file
|
||||
hosts = "sudo nvim /etc/hosts";
|
||||
# Default shell setting doesn't work
|
||||
home.sessionVariables = {
|
||||
SHELL = "${pkgs.fish}/bin/fish";
|
||||
};
|
||||
|
||||
programs.fish = {
|
||||
shellAbbrs = {
|
||||
# Shortcut to edit hosts file
|
||||
hosts = "sudo hx /etc/hosts";
|
||||
};
|
||||
shellInit = ''
|
||||
set -g __nixos_path_original $PATH
|
||||
function __nixos_path_fix -d "fix PATH value"
|
||||
set -l result (string split ":" $__nixos_path_original)
|
||||
for elt in $PATH
|
||||
if not contains -- $elt $result
|
||||
set -a result $elt
|
||||
end
|
||||
end
|
||||
set -g PATH $result
|
||||
end
|
||||
__nixos_path_fix
|
||||
'';
|
||||
|
||||
# # Speeds up fish launch time on macOS
|
||||
# useBabelfish = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -114,10 +114,17 @@ in
|
||||
dr = "docker run --rm -it";
|
||||
db = "docker build . -t";
|
||||
};
|
||||
shellInit = "";
|
||||
shellInit = ''
|
||||
# Fix for lagging/hanging after exiting TUIs inside terminal multiplexers like Zellij:
|
||||
# Disable fish terminal querying at startup so fish does not query DA1/termcap on return.
|
||||
set -gx fish_features no-query-term
|
||||
'';
|
||||
};
|
||||
|
||||
home.sessionVariables.fish_greeting = "";
|
||||
home.sessionVariables = {
|
||||
fish_greeting = "";
|
||||
fish_features = "no-query-term";
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ in
|
||||
programs.ghostty = {
|
||||
enable = true;
|
||||
|
||||
package = if pkgs.stdenv.isDarwin then null else pkgs.ghostty;
|
||||
package = if pkgs.stdenv.hostPlatform.isDarwin then pkgs.ghostty-bin else pkgs.ghostty;
|
||||
|
||||
enableFishIntegration = true;
|
||||
enableFishIntegration = false; # Handled conditionally below to avoid conflicts inside Zellij/TMUX
|
||||
enableBashIntegration = true;
|
||||
enableZshIntegration = true;
|
||||
installBatSyntax = false; # The file doesn't seem to exist in the pkg
|
||||
@@ -33,18 +33,21 @@ in
|
||||
macos-titlebar-style = "hidden";
|
||||
window-decoration = false;
|
||||
macos-non-native-fullscreen = true;
|
||||
quit-after-last-window-closed = lib.mkIf pkgs.stdenv.isDarwin true;
|
||||
fullscreen = if pkgs.stdenv.isDarwin then true else false;
|
||||
quit-after-last-window-closed = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin true;
|
||||
fullscreen = if pkgs.stdenv.hostPlatform.isDarwin then true else false;
|
||||
keybind = [
|
||||
"super+t=unbind" # Pass super-t to underlying tool (e.g. zellij tabs)
|
||||
"super+shift+bracket_right=unbind"
|
||||
"super+shift+bracket_left=unbind"
|
||||
"ctrl+tab=unbind"
|
||||
"ctrl+shift+tab=unbind"
|
||||
"ctrl+tab=text:\\x1b[9;5u"
|
||||
"ctrl+shift+tab=text:\\x1b[9;6u"
|
||||
"super+k=unbind"
|
||||
"super+shift+e=unbind"
|
||||
# Translate Mac Super & Ctrl combinations into Alt (ESC prefix) sequences
|
||||
# so Zellij receives them without needing Kitty keyboard protocol
|
||||
"super+t=text:\\x1bt"
|
||||
"super+shift+]=text:\\x1b}"
|
||||
"super+shift+[=text:\\x1b{"
|
||||
"ctrl+tab=text:\\x1b}"
|
||||
"ctrl+shift+tab=text:\\x1b{"
|
||||
"super+k=text:\\x1bK"
|
||||
"super+shift+e=text:\\x1bE"
|
||||
# Send CSI-u sequence for Alt+Shift+P / Super+Shift+P to bypass Zellij 0.45's DCS (ESC P) parser timeout (~1.5s delay)
|
||||
"alt+shift+p=text:\\x1b[112;4u"
|
||||
"super+shift+p=text:\\x1b[112;4u"
|
||||
];
|
||||
};
|
||||
themes."gruvbox" = {
|
||||
@@ -81,5 +84,12 @@ in
|
||||
|
||||
};
|
||||
|
||||
# Conditionally enable Ghostty fish shell integration only when NOT running inside multiplexers like Zellij/TMUX
|
||||
programs.fish.shellInit = ''
|
||||
if set -q GHOSTTY_RESOURCES_DIR; and not set -q ZELLIJ; and not set -q TMUX
|
||||
source "$GHOSTTY_RESOURCES_DIR/shell-integration/fish/vendor_conf.d/ghostty-shell-integration.fish"
|
||||
end
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,8 +39,19 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
programs.git = {
|
||||
userName = lib.mkForce cfg.work.name;
|
||||
userEmail = lib.mkForce cfg.work.email;
|
||||
settings = {
|
||||
user = {
|
||||
name = lib.mkForce cfg.work.name;
|
||||
email = lib.mkForce cfg.work.email;
|
||||
signingKey = "~/.ssh/work_github";
|
||||
};
|
||||
commit = {
|
||||
gpgsign = true;
|
||||
};
|
||||
tag = {
|
||||
gpgsign = true;
|
||||
};
|
||||
};
|
||||
includes = [
|
||||
{
|
||||
path = "${config.home.homeDirectory}/${config.xdg.configFile."git/personal".target}";
|
||||
@@ -50,6 +61,11 @@ in
|
||||
|
||||
};
|
||||
|
||||
# Add work to signers file
|
||||
xdg.configFile."git/allowed-signers".text = ''
|
||||
${cfg.work.email} ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIP7aXbmKHmWUZgwG5HPtwx+nREVeMIRplpAAzxPOFXL
|
||||
'';
|
||||
|
||||
# Personal git config
|
||||
xdg.configFile."git/personal".text = lib.generators.toGitINI {
|
||||
user = {
|
||||
@@ -57,12 +73,6 @@ in
|
||||
email = cfg.personal.email;
|
||||
signingkey = "~/.ssh/id_ed25519";
|
||||
};
|
||||
commit = {
|
||||
gpgsign = true;
|
||||
};
|
||||
tag = {
|
||||
gpgsign = true;
|
||||
};
|
||||
};
|
||||
|
||||
# Personal jj config
|
||||
|
||||
@@ -29,9 +29,11 @@ in
|
||||
|
||||
programs.git = {
|
||||
enable = true;
|
||||
userName = cfg.name;
|
||||
userEmail = cfg.email;
|
||||
extraConfig = {
|
||||
settings = {
|
||||
user = {
|
||||
name = cfg.name;
|
||||
email = cfg.email;
|
||||
};
|
||||
core.pager = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight | less --no-init";
|
||||
interactive.difffilter = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight";
|
||||
pager = {
|
||||
@@ -63,7 +65,7 @@ in
|
||||
};
|
||||
|
||||
xdg.configFile."git/allowed-signers".text = ''
|
||||
7386960+nmasur@users.noreply.github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s
|
||||
${config.nmasur.presets.programs.git.email} ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s
|
||||
'';
|
||||
|
||||
programs.fish.shellAbbrs = {
|
||||
|
||||
@@ -22,7 +22,6 @@ in
|
||||
extensions = [
|
||||
pkgs.nmasur.gh-collaborators
|
||||
pkgs.gh-dash
|
||||
pkgs.gh-copilot
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -7,6 +7,62 @@
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs.helix;
|
||||
|
||||
blame_file_pretty = pkgs.writeShellScriptBin "blame_file_pretty" ''
|
||||
# Source: https://gist.github.com/gloaysa/828707f067e3bb20da18d72fa5d4963a
|
||||
# Utility for Helix: open the patch for the commit that last touched the current line.
|
||||
# If the line isn’t committed yet, it shows the working-tree diff for THIS file only.
|
||||
# The script writes the diff to /tmp and prints the absolute path to stdout
|
||||
# Adjust `context` to see more/fewer unchanged lines around the change (default: 3).
|
||||
#
|
||||
# usage: git-file_pretty.sh <file> <line> [context_lines]
|
||||
# Helix mapping example:
|
||||
# B = ':open %sh{ ~/.config/helix/utils/git-blame-commit.sh "%{buffer_name}" %{cursor_line} 3 }'
|
||||
file="$1"
|
||||
line="$2"
|
||||
ctx="''${3:-3}"
|
||||
|
||||
# blame the exact line
|
||||
porc="$(git blame -L "$line",+1 --porcelain -- "$file")" || exit 1
|
||||
sha="$(printf '%s\n' "$porc" | awk 'NR==1{print $1}')"
|
||||
commit_path="$(printf '%s\n' "$porc" | awk '/^filename /{print substr($0,10); exit}')"
|
||||
|
||||
out="/tmp/hx-blame_$(basename "$file")_''${sha:-wt}.diff"
|
||||
|
||||
if [ -z "$sha" ] || [ "$sha" = 0000000000000000000000000000000000000000 ] || [ "$sha" = "^" ]; then
|
||||
# uncommitted line → working tree diff for this file
|
||||
git --no-pager diff --no-color -U"$ctx" -- "$file" > "$out"
|
||||
else
|
||||
# committed line → only this file’s patch in that commit
|
||||
git --no-pager show --no-color -M -C -U"$ctx" "$sha" -- "''${commit_path:-$file}" > "$out"
|
||||
fi
|
||||
|
||||
# "return" the path for :open %sh{…}
|
||||
printf '%s' "$out"
|
||||
'';
|
||||
|
||||
blame_line_pretty = pkgs.writeShellScriptBin "blame_line_pretty" ''
|
||||
# Source: https://gist.github.com/gloaysa/828707f067e3bb20da18d72fa5d4963a
|
||||
# Utility for Helix: pretty-print blame info for the line under the cursor.
|
||||
# Quite basic.
|
||||
#
|
||||
# usage: blame_line_pretty <file> <line>
|
||||
# Helix mapping example:
|
||||
# b = ":run-shell-command ~/.config/helix/utils/blame_line_pretty.sh %{buffer_name} %{cursor_line}"
|
||||
file="$1"; line="$2"
|
||||
out="$(git blame -L "$line",+1 --porcelain -- "$file")" || return 1
|
||||
|
||||
sha="$(printf '%s\n' "$out" | awk 'NR==1{print $1}')"
|
||||
author="$(printf '%s\n' "$out" | awk -F'author ' '/^author /{print $2; exit}')"
|
||||
epoch="$(printf '%s\n' "$out" | awk '/^author-time /{print $2; exit}')"
|
||||
# dd-mm-yyyy (macOS `date -r`; fallback to gdate if present)
|
||||
date="$( (date -r "$epoch" +%d-%m-%Y\ %H:%M 2>/dev/null) || (gdate -d "@$epoch" +%d-%m-%Y\ %H:%M 2>/dev/null) || printf '%s' "$epoch")"
|
||||
summary="$(printf '%s\n' "$out" | awk -F'summary ' '/^summary /{print $2; exit}')"
|
||||
change="$(printf '%s\n' "$out" | tail -n 1)"
|
||||
|
||||
printf "%s\n%s\n%s\n%s\n%s\n" "$sha" "$author" "$date" "$summary" "$change"
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@@ -16,13 +72,13 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
# Use Neovim as the editor for git commit messages
|
||||
programs.git.extraConfig.core.editor = lib.mkForce "${lib.getExe pkgs.helix}";
|
||||
programs.git.settings.core.editor = lib.mkForce "${lib.getExe pkgs.helix}";
|
||||
programs.jujutsu.settings.ui.editor = lib.mkForce "${lib.getExe pkgs.helix}";
|
||||
|
||||
# Set Neovim as the default app for text editing and manual pages
|
||||
home.sessionVariables = {
|
||||
EDITOR = lib.mkForce "${lib.getExe pkgs.helix}";
|
||||
MANPAGER = lib.mkForce "sh -c 'col -bx | ${lib.getExe pkgs.helix}'";
|
||||
# MANPAGER = lib.mkForce "sh -c 'col -bx | ${lib.getExe pkgs.helix}'";
|
||||
MANWIDTH = 87;
|
||||
MANROFFOPT = "-c";
|
||||
};
|
||||
@@ -62,6 +118,11 @@ in
|
||||
command = lib.getExe pkgs.marksman;
|
||||
};
|
||||
|
||||
language-server.rumdl = {
|
||||
command = lib.getExe pkgs.rumdl;
|
||||
args = [ "server" ];
|
||||
};
|
||||
|
||||
language-server.terraform-ls = {
|
||||
command = "${lib.getExe pkgs.terraform-ls}";
|
||||
args = [ "serve" ];
|
||||
@@ -87,10 +148,16 @@ in
|
||||
{
|
||||
name = "markdown";
|
||||
auto-format = false;
|
||||
language-servers = [ "marksman" ];
|
||||
language-servers = [
|
||||
"marksman"
|
||||
"rumdl"
|
||||
];
|
||||
formatter = {
|
||||
command = lib.getExe pkgs.mdformat;
|
||||
args = [ "-" ];
|
||||
command = lib.getExe pkgs.rumdl;
|
||||
args = [
|
||||
"fmt"
|
||||
"-"
|
||||
];
|
||||
};
|
||||
# Allows return key to continue the token on the next line
|
||||
comment-tokens = [
|
||||
@@ -102,9 +169,18 @@ in
|
||||
];
|
||||
}
|
||||
{
|
||||
name = "tfvars";
|
||||
name = "hcl";
|
||||
scope = "source.hcl";
|
||||
# injection-regex = "terraform";
|
||||
auto-format = true;
|
||||
language-servers = [ "terraform-ls" ];
|
||||
language-id = "terraform";
|
||||
file-types = [
|
||||
"tf"
|
||||
"tfvars"
|
||||
"terraform"
|
||||
{ glob = "*.auto.tfvars"; }
|
||||
];
|
||||
formatter = {
|
||||
command = lib.getExe pkgs.terraform;
|
||||
args = [
|
||||
@@ -114,11 +190,15 @@ in
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "hcl";
|
||||
name = "hcl-packer";
|
||||
scope = "source.hcl-packer";
|
||||
auto-format = true;
|
||||
language-servers = [ "terraform-ls" ];
|
||||
file-types = [
|
||||
"hcl"
|
||||
"pkr.hcl"
|
||||
];
|
||||
formatter = {
|
||||
command = lib.getExe pkgs.terraform;
|
||||
command = "${pkgs.packer}/bin/packer";
|
||||
args = [
|
||||
"fmt"
|
||||
"-"
|
||||
@@ -179,18 +259,9 @@ in
|
||||
];
|
||||
|
||||
# Commandline git blame
|
||||
space.B = ":echo %sh{git log -n1 --date=short --pretty=format:'%%h %%ad %%s' $(git blame -L %{cursor_line},+1 \"%{buffer_name}\" | cut -d' ' -f1)}";
|
||||
|
||||
# Open yazi
|
||||
# https://github.com/sxyazi/yazi/pull/2461
|
||||
# Won't work until next Helix release
|
||||
C-y = [
|
||||
":sh rm -f /tmp/unique-file"
|
||||
":insert-output ${lib.getExe pkgs.yazi} %{buffer_name} --chooser-file=/tmp/unique-file"
|
||||
":insert-output echo \\x1b[?1049h\\x1b[?2004h > /dev/tty"
|
||||
":open %sh{cat /tmp/unique-file}"
|
||||
":redraw"
|
||||
];
|
||||
# space.B = ":echo %sh{git log -n1 --date=short --pretty=format:'%%h %%ad %%s' $(git blame -L %{cursor_line},+1 \"%{buffer_name}\" | cut -d' ' -f1)}";
|
||||
space.B = '':open %sh{ ${blame_line_pretty}/bin/blame_line_pretty "%{buffer_name}" %{cursor_line} 3 }'';
|
||||
space.i = '':open %sh{ ${blame_file_pretty}/bin/blame_file_pretty "%{buffer_name}" %{cursor_line} 3 }'';
|
||||
|
||||
# Extend selection above
|
||||
X = "select_line_above";
|
||||
@@ -256,6 +327,10 @@ in
|
||||
git-exclude = true; # Skip excluded files
|
||||
};
|
||||
|
||||
completion-replace = true; # Replace whole word with completion
|
||||
trim-trailing-whitespace = true;
|
||||
# rainbow-brackets = true; # Make it easier to match parentheses
|
||||
|
||||
# Show whitespace visible to the user
|
||||
# Waiting for trailing whitespace option ideally
|
||||
whitespace = {
|
||||
@@ -272,7 +347,7 @@ in
|
||||
|
||||
};
|
||||
|
||||
themes."${config.programs.helix.settings.theme}" = {
|
||||
themes.base16 = {
|
||||
"attributes" = config.theme.colors.base09;
|
||||
"comment" = {
|
||||
fg = config.theme.colors.base03;
|
||||
@@ -452,8 +527,220 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
themes.alabaster-style = {
|
||||
"attribute" = config.theme.colors.base05;
|
||||
"comment" = {
|
||||
fg = config.theme.colors.base0A;
|
||||
# modifiers = [ "italic" ];
|
||||
};
|
||||
"constant" = config.theme.colors.base0E;
|
||||
"constant.numeric" = config.theme.colors.base0E;
|
||||
"constant.builtin" = config.theme.colors.base0E;
|
||||
"constant.character" = config.theme.colors.base0E;
|
||||
"constant.character.escape" = config.theme.colors.base0C;
|
||||
"constructor" = config.theme.colors.base0D;
|
||||
"debug" = config.theme.colors.base03;
|
||||
"diagnostic" = {
|
||||
modifiers = [ "underlined" ];
|
||||
};
|
||||
"diff.delta" = config.theme.colors.base09;
|
||||
"diff.minus" = config.theme.colors.base08;
|
||||
"diff.plus" = config.theme.colors.base0B;
|
||||
"error" = config.theme.colors.base08;
|
||||
"function" = config.theme.colors.base0D;
|
||||
"hint" = config.theme.colors.base03;
|
||||
"info" = config.theme.colors.base0D;
|
||||
"keyword" = config.theme.colors.base05;
|
||||
"keyword.control" = config.theme.colors.base05;
|
||||
"keyword.operator" = config.theme.colors.base05;
|
||||
"label" = config.theme.colors.base0E;
|
||||
"namespace" = config.theme.colors.base0E;
|
||||
"operator" = config.theme.colors.base05;
|
||||
"punctuation" = config.theme.colors.base04;
|
||||
"punctuation.bracket" = config.theme.colors.base04;
|
||||
"punctuation.delimiter" = config.theme.colors.base04;
|
||||
"special" = config.theme.colors.base0D;
|
||||
"string" = config.theme.colors.base0B;
|
||||
"string.regexp" = config.theme.colors.base0B;
|
||||
"string.special" = config.theme.colors.base0C;
|
||||
"type" = config.theme.colors.base0A;
|
||||
"variable" = config.theme.colors.base05;
|
||||
"variable.parameter" = config.theme.colors.base05;
|
||||
"variable.builtin" = config.theme.colors.base05;
|
||||
"variable.other.member" = config.theme.colors.base05;
|
||||
"warning" = config.theme.colors.base09;
|
||||
"markup.bold" = {
|
||||
fg = config.theme.colors.base0A;
|
||||
modifiers = [ "bold" ];
|
||||
};
|
||||
"markup.heading" = config.theme.colors.base0D;
|
||||
"markup.italic" = {
|
||||
fg = config.theme.colors.base0E;
|
||||
modifiers = [ "italic" ];
|
||||
};
|
||||
"markup.link.text" = config.theme.colors.base08;
|
||||
"markup.link.url" = {
|
||||
fg = config.theme.colors.base09;
|
||||
modifiers = [ "underlined" ];
|
||||
};
|
||||
"markup.list" = config.theme.colors.base08;
|
||||
"markup.quote" = config.theme.colors.base0C;
|
||||
"markup.raw" = config.theme.colors.base0B;
|
||||
"markup.strikethrough" = {
|
||||
modifiers = [ "crossed_out" ];
|
||||
};
|
||||
"diagnostic.hint" = {
|
||||
underline = {
|
||||
style = "curl";
|
||||
};
|
||||
};
|
||||
"diagnostic.info" = {
|
||||
underline = {
|
||||
style = "curl";
|
||||
};
|
||||
};
|
||||
"diagnostic.warning" = {
|
||||
underline = {
|
||||
style = "curl";
|
||||
};
|
||||
};
|
||||
"diagnostic.error" = {
|
||||
underline = {
|
||||
style = "curl";
|
||||
};
|
||||
};
|
||||
"ui.background" = {
|
||||
bg = config.theme.colors.base00;
|
||||
};
|
||||
"ui.bufferline.active" = {
|
||||
fg = config.theme.colors.base00;
|
||||
bg = config.theme.colors.base03;
|
||||
modifiers = [ "bold" ];
|
||||
};
|
||||
"ui.bufferline" = {
|
||||
fg = config.theme.colors.base04;
|
||||
bg = config.theme.colors.base00;
|
||||
};
|
||||
"ui.cursor" = {
|
||||
fg = config.theme.colors.base04;
|
||||
modifiers = [ "reversed" ];
|
||||
};
|
||||
"ui.cursor.insert" = {
|
||||
fg = config.theme.colors.base0A;
|
||||
modifiers = [ "reversed" ];
|
||||
};
|
||||
"ui.cursorline.primary" = {
|
||||
fg = config.theme.colors.base05;
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.cursor.match" = {
|
||||
fg = config.theme.colors.base03;
|
||||
modifiers = [ "reversed" ];
|
||||
};
|
||||
"ui.cursor.select" = {
|
||||
fg = config.theme.colors.base04;
|
||||
modifiers = [ "reversed" ];
|
||||
};
|
||||
"ui.gutter" = {
|
||||
bg = config.theme.colors.base00;
|
||||
};
|
||||
"ui.help" = {
|
||||
fg = config.theme.colors.base06;
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.linenr" = {
|
||||
fg = config.theme.colors.base03;
|
||||
bg = config.theme.colors.base00;
|
||||
};
|
||||
"ui.linenr.selected" = {
|
||||
fg = config.theme.colors.base04;
|
||||
bg = config.theme.colors.base01;
|
||||
modifiers = [ "bold" ];
|
||||
};
|
||||
"ui.menu" = {
|
||||
fg = config.theme.colors.base05;
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.menu.scroll" = {
|
||||
fg = config.theme.colors.base03;
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.menu.selected" = {
|
||||
fg = config.theme.colors.base01;
|
||||
bg = config.theme.colors.base04;
|
||||
};
|
||||
"ui.popup" = {
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.selection" = {
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.selection.primary" = {
|
||||
bg = config.theme.colors.base02;
|
||||
};
|
||||
"ui.statusline" = {
|
||||
fg = config.theme.colors.base04;
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.statusline.inactive" = {
|
||||
bg = config.theme.colors.base01;
|
||||
fg = config.theme.colors.base03;
|
||||
};
|
||||
"ui.statusline.insert" = {
|
||||
fg = config.theme.colors.base00;
|
||||
bg = config.theme.colors.base0B;
|
||||
};
|
||||
"ui.statusline.normal" = {
|
||||
fg = config.theme.colors.base00;
|
||||
bg = config.theme.colors.base03;
|
||||
};
|
||||
"ui.statusline.select" = {
|
||||
fg = config.theme.colors.base00;
|
||||
bg = config.theme.colors.base0F;
|
||||
};
|
||||
"ui.text" = config.theme.colors.base05;
|
||||
"ui.text.focus" = config.theme.colors.base05;
|
||||
"ui.virtual.indent-guide" = {
|
||||
fg = config.theme.colors.base03;
|
||||
};
|
||||
"ui.virtual.inlay-hint" = {
|
||||
fg = config.theme.colors.base03;
|
||||
};
|
||||
"ui.virtual.ruler" = {
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
"ui.virtual.jump-label" = {
|
||||
fg = config.theme.colors.base0A;
|
||||
modifiers = [ "bold" ];
|
||||
};
|
||||
"ui.window" = {
|
||||
bg = config.theme.colors.base01;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
# Create a desktop option for launching Helix from a file manager
|
||||
# (Requires launching the terminal and then executing Helix)
|
||||
xdg.desktopEntries.helix =
|
||||
lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && config.nmasur.presets.services.i3.enable)
|
||||
{
|
||||
name = "Helix wrapper";
|
||||
exec = ''sh -c "${lib.getExe config.nmasur.presets.services.i3.terminal} --command='hx \$1'" _ %F ''; # TODO: change to work for any terminal
|
||||
mimeType = [
|
||||
"text/plain"
|
||||
"text/markdown"
|
||||
];
|
||||
};
|
||||
xdg.mimeApps.defaultApplications = {
|
||||
"text/plain" = lib.mkBefore [ "Helix.desktop" ];
|
||||
"text/markdown" = lib.mkBefore [ "Helix.desktop" ];
|
||||
};
|
||||
|
||||
home.packages = [
|
||||
(pkgs.writers.writeDashBin "xterm" ''${lib.getExe config.nmasur.presets.services.i3.terminal} +new-window --command"$@" '')
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs.homebrew;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.programs.homebrew.enable =
|
||||
lib.mkEnableOption "Homebrew macOS package manager";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.file.".Brewfile".text = /* homebrew */ ''
|
||||
# Taps
|
||||
# tap "homebrew/bundle"
|
||||
|
||||
# Brews (CLI Apps)
|
||||
brew "trash" # Delete files and folders to trash instead of rm
|
||||
|
||||
# Casks (GUI Apps)
|
||||
cask "scroll-reverser" # Different scroll style for mouse vs. trackpad
|
||||
cask "notunes" # Don't launch Apple Music with the play button
|
||||
cask "topnotch" # Darkens the menu bar to complete black
|
||||
cask "ghostty" # Terminal application (not buildable on Nix on macOS)
|
||||
cask "discord" # nixpkgs bundle breaks its notarization seal on macOS 26+
|
||||
|
||||
# Mac App Store apps (requires 'mas' CLI, optional)
|
||||
# mas "Tailscale", id: 1475387142
|
||||
'';
|
||||
|
||||
# Add homebrew paths to CLI path
|
||||
home.sessionPath = [
|
||||
"/opt/homebrew/bin/"
|
||||
"/opt/homebrew/opt/trash/bin"
|
||||
];
|
||||
|
||||
programs.fish.shellAbbrs.t = "trash";
|
||||
|
||||
home.activation.backendBrewBundle = /* bash */ ''
|
||||
# Requires Homebrew to be installed
|
||||
if ! /usr/bin/xcode-select --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD /usr/bin/xcode-select --install
|
||||
fi
|
||||
if ! /opt/homebrew/bin/brew --version 2>/dev/null; then
|
||||
$DRY_RUN_CMD /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
fi
|
||||
|
||||
verboseEcho "Syncing Homebrew dependencies via Brewfile..."
|
||||
|
||||
# Ensure Homebrew is in the PATH for the script (Apple Silicon path)
|
||||
export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH"
|
||||
|
||||
# Keep activation deterministic and non-interactive. Without this, brew
|
||||
# auto-updates (git-fetches its taps) which can hang forever when there
|
||||
# is no TTY (network stall, credential prompt, or a leftover brew lock).
|
||||
export HOMEBREW_NO_AUTO_UPDATE=1
|
||||
export HOMEBREW_NO_ENV_HINTS=1
|
||||
|
||||
# Point brew at the generated Brewfile in the Nix store directly. This
|
||||
# avoids depending on the ~/.Brewfile symlink being linked first, and
|
||||
# sidesteps the --global vs. inherited HOMEBREW_BUNDLE_FILE conflict.
|
||||
unset HOMEBREW_BUNDLE_FILE
|
||||
brewfile="${config.home.file.".Brewfile".source}"
|
||||
|
||||
if command -v brew &> /dev/null; then
|
||||
# Install/upgrade everything declared in the Brewfile...
|
||||
$DRY_RUN_CMD brew bundle install --file="$brewfile"
|
||||
# ...then uninstall anything NOT listed (replaces deprecated --cleanup).
|
||||
$DRY_RUN_CMD brew bundle cleanup --file="$brewfile" --force
|
||||
else
|
||||
verboseEcho "Warning: Homebrew not found. Actions skipped."
|
||||
fi
|
||||
|
||||
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs.jujutsu;
|
||||
tomlFormat = pkgs.formats.toml { };
|
||||
in
|
||||
|
||||
{
|
||||
@@ -20,8 +21,8 @@ in
|
||||
# https://github.com/martinvonz/jj/blob/main/docs/config.md
|
||||
settings = {
|
||||
user = {
|
||||
name = config.programs.git.userName;
|
||||
email = config.programs.git.userEmail;
|
||||
name = config.programs.git.settings.user.name;
|
||||
email = config.programs.git.settings.user.email;
|
||||
};
|
||||
ui.paginate = "never";
|
||||
|
||||
@@ -31,9 +32,37 @@ in
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."jjui/config.toml".source = tomlFormat.generate "jjui-config" {
|
||||
actions = [
|
||||
{
|
||||
name = "set-github-bypass";
|
||||
lua = ''
|
||||
exec_shell([[gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/properties/values -f 'properties[][property_name]=Allow-Ruleset-Bypass' -f 'properties[][value]=true']])
|
||||
flash("Set GitHub rule bypass")
|
||||
'';
|
||||
key = "ctrl+b";
|
||||
scope = "revisions";
|
||||
desc = "Set GitHub rule bypass";
|
||||
}
|
||||
{
|
||||
name = "remove-github-bypass";
|
||||
lua = ''
|
||||
exec_shell([[gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/properties/values -f 'properties[][property_name]=Allow-Ruleset-Bypass' -f 'properties[][value]=']])
|
||||
flash("Removed GitHub rule bypass")
|
||||
'';
|
||||
key = "ctrl+shift+b";
|
||||
scope = "revisions";
|
||||
desc = "Remove GitHub rule bypass";
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
home.packages = [
|
||||
# Required for the fsmonitor to auto-snapshot
|
||||
pkgs.watchman
|
||||
|
||||
# Required to be on path to work in Zellij
|
||||
pkgs.jjui
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ in
|
||||
nmasur.presets.services.i3.terminal = pkgs.kitty;
|
||||
|
||||
# Set the Rofi terminal for running programs
|
||||
programs.rofi.terminal = lib.mkIf pkgs.stdenv.isLinux (lib.mkDefault "${pkgs.kitty}/bin/kitty");
|
||||
programs.rofi.terminal = lib.mkIf pkgs.stdenv.hostPlatform.isLinux (lib.mkDefault "${pkgs.kitty}/bin/kitty");
|
||||
|
||||
# Display images in the terminal
|
||||
programs.fish.interactiveShellInit = # fish
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs.lag-triage;
|
||||
|
||||
term-probe = pkgs.writeScriptBin "term-probe" ''
|
||||
#!${lib.getExe pkgs.python3}
|
||||
${builtins.readFile ./term_probe.py}
|
||||
'';
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.programs.lag-triage.enable =
|
||||
lib.mkEnableOption "Terminal input-lag triage tools";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.packages = [ term-probe ];
|
||||
|
||||
programs.fish.functions = {
|
||||
lag-triage = {
|
||||
description = "Diagnose post-TUI typing lag in the current shell";
|
||||
body = builtins.readFile ./lag-triage.fish;
|
||||
};
|
||||
unlag = {
|
||||
description = "Reset terminal state left behind by a TUI";
|
||||
body = builtins.readFile ./unlag.fish;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
# Guided diagnosis for the post-TUI typing-lag problem (Ghostty + Zellij + fish).
|
||||
# Run this IN THE LAGGING SHELL the moment you notice the lag, BEFORE starting
|
||||
# a new shell. It captures evidence, then applies targeted resets one at a time
|
||||
# so the stage that cures the lag identifies the layer holding stuck state.
|
||||
# Everything is logged for filing an upstream issue.
|
||||
|
||||
set -l logdir ~/.local/state/lag-triage
|
||||
mkdir -p $logdir
|
||||
set -l logfile $logdir/(date +%Y%m%d-%H%M%S).log
|
||||
|
||||
function _lt --inherit-variable logfile
|
||||
echo $argv | tee -a $logfile
|
||||
end
|
||||
|
||||
function _lt_ask --inherit-variable logfile
|
||||
# usage: _lt_ask VARNAME prompt... -> sets global $VARNAME (default: skip)
|
||||
set -l __name $argv[1]
|
||||
read -g -P "$argv[2..] " $__name
|
||||
or set -g $__name skip
|
||||
test -z "$$__name"; and set -g $__name skip
|
||||
echo "ANSWER $__name: $$__name" >>$logfile
|
||||
end
|
||||
|
||||
_lt "== lag-triage "(date)" =="
|
||||
_lt "Log: $logfile"
|
||||
_lt "Answer y / n, or press Enter to skip a question."
|
||||
_lt ""
|
||||
|
||||
# ---- 1. Context -------------------------------------------------------------
|
||||
_lt_ask ans_tui "Which TUI did you just exit (nvim/jjui/yazi/other)?"
|
||||
_lt_ask ans_launch "Launched via (f)loating-pane keybind or (c)ommand typed in this shell?"
|
||||
|
||||
# ---- 2. Snapshot ------------------------------------------------------------
|
||||
begin
|
||||
echo "-- snapshot --"
|
||||
fish --version
|
||||
echo "fish pid: $fish_pid, started: "(ps -o lstart= -p $fish_pid 2>/dev/null)
|
||||
zellij --version 2>/dev/null
|
||||
echo "escape delay: '$fish_escape_delay_ms' sequence delay: '$fish_sequence_key_delay_ms'"
|
||||
env | grep -iE '^(TERM|ZELLIJ|GHOSTTY|COLORTERM)' | sort
|
||||
echo "-- status features --"
|
||||
status features
|
||||
echo "-- stty -a --"
|
||||
stty -a
|
||||
end >>$logfile 2>&1
|
||||
_lt "Captured shell + environment snapshot."
|
||||
|
||||
# Proven root cause of the 2026-08 lag (see docs/CHANGELOG.md 2026-08-29):
|
||||
# fish latches feature flags from its startup env before config.fish runs, so
|
||||
# a shell with query-term ON sends terminal queries after every command; one
|
||||
# reply zellij fails to relay permanently degrades this process's reader.
|
||||
if status features | string match -qr '^query-term\s+on'
|
||||
_lt ""
|
||||
_lt "!! query-term is ON in this shell: fish did NOT get fish_features="
|
||||
_lt "!! no-query-term in its STARTUP environment (config.fish is too late)."
|
||||
_lt "!! This is the proven root cause of the post-TUI lag — a query reply"
|
||||
_lt "!! lost by zellij permanently degrades this fish process's reader."
|
||||
_lt "!! Fix: spawn fish with the variable exported (zellij default_shell"
|
||||
_lt "!! wrapper fish-no-query-term). Subshells are immune because they"
|
||||
_lt "!! inherit the exported variable — that's why a new shell 'fixes' it."
|
||||
else
|
||||
_lt "query-term is off in this shell (good — the known root cause is ruled out)."
|
||||
end
|
||||
|
||||
# ---- 3. Terminal state below the shell --------------------------------------
|
||||
_lt ""
|
||||
_lt "Querying terminal state (takes a few seconds)..."
|
||||
term-probe report 2>&1 | tee -a $logfile
|
||||
_lt ""
|
||||
_lt " ^ Things to look for: kitty flags with a reply > 0, modifyOtherKeys > 1,"
|
||||
_lt " any mouse/alternate-screen mode SET while at a shell prompt, or a slow"
|
||||
_lt " DA1 round-trip (> 100 ms means the input path itself is delayed)."
|
||||
|
||||
# ---- 4. Raw keystroke capture (bypasses fish entirely) ----------------------
|
||||
_lt ""
|
||||
_lt "Raw input capture: type ~10 characters at a steady pace, including one"
|
||||
_lt "ESC press and one arrow key. This shows the exact bytes this pane delivers"
|
||||
_lt "and their timing, with fish's input handling out of the picture."
|
||||
term-probe keylog 2>&1 | tee -a $logfile
|
||||
_lt_ask ans_keylog_instant "Did each keypress appear INSTANTLY in the capture? (y/n)"
|
||||
_lt_ask ans_keylog_plain "Were plain letters single plain bytes like b'a' (not escape sequences)? (y/n)"
|
||||
|
||||
# ---- 5. Scope ---------------------------------------------------------------
|
||||
_lt ""
|
||||
_lt_ask ans_scope_pane "Optional: open a NEW zellij pane/tab and type — laggy there too? (y/n)"
|
||||
_lt_ask ans_scope_window "Optional: type in a separate Ghostty window (outside this zellij session) — laggy? (y/n)"
|
||||
|
||||
# ---- 6. Staged resets -------------------------------------------------------
|
||||
# Each stage resets one category of state a TUI could have left behind.
|
||||
# The first stage that cures the lag names the culprit.
|
||||
set -l fixed none
|
||||
|
||||
_lt ""
|
||||
_lt "Now applying resets one at a time. After each, type into the test prompt"
|
||||
_lt "to judge whether the lag is gone."
|
||||
_lt "CAVEAT: fish's read prompt may NOT exhibit lag even when the main"
|
||||
_lt "commandline does. If typing at these test prompts never feels laggy at"
|
||||
_lt "all, answer 'u' (unsure) instead of 'y' — a 'y' here is only meaningful"
|
||||
_lt "if you could feel the lag at the test prompts before the reset."
|
||||
|
||||
if test $fixed = none
|
||||
_lt ""
|
||||
_lt "Stage A - kitty keyboard protocol: pop stack + clear all flags"
|
||||
printf '\e[<9u\e[=0;1u'
|
||||
_lt_ask ans_stage_a " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_a" = y; and set fixed "A (kitty keyboard state)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage B - modifyOtherKeys off"
|
||||
printf '\e[>4;0m'
|
||||
_lt_ask ans_stage_b " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_b" = y; and set fixed "B (modifyOtherKeys)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage C - normal keypad + normal cursor keys"
|
||||
printf '\e>\e[?1l'
|
||||
_lt_ask ans_stage_c " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_c" = y; and set fixed "C (application keypad/cursor mode)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage D - disable mouse, focus reporting, synchronized output"
|
||||
printf '\e[?1000l\e[?1001l\e[?1002l\e[?1003l\e[?1005l\e[?1006l\e[?1015l\e[?1016l\e[?1004l\e[?2026l'
|
||||
_lt_ask ans_stage_d " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_d" = y; and set fixed "D (mouse/focus/sync modes)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage E - leave alternate screen"
|
||||
printf '\e[?1049l'
|
||||
_lt_ask ans_stage_e " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_e" = y; and set fixed "E (alternate screen)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage F - stty sane (line-discipline reset)"
|
||||
stty sane
|
||||
_lt_ask ans_stage_f " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_f" = y; and set fixed "F (termios/line discipline)"
|
||||
end
|
||||
|
||||
if test $fixed = none
|
||||
_lt "Stage G - DECSTR soft terminal reset"
|
||||
printf '\e[!p'
|
||||
_lt_ask ans_stage_g " Test typing here, then Enter — lag gone? (y/n)"
|
||||
test "$ans_stage_g" = y; and set fixed "G (DECSTR-resettable mode)"
|
||||
end
|
||||
|
||||
# ---- 7. Verdict --------------------------------------------------------------
|
||||
_lt ""
|
||||
_lt "== Verdict =="
|
||||
if test $fixed != none
|
||||
_lt "Lag cleared by stage $fixed."
|
||||
_lt "That state was stuck BELOW fish — in the Zellij pane or relayed to"
|
||||
_lt "Ghostty — and the TUI you exited ($ans_tui) failed to restore it, or"
|
||||
_lt "Zellij failed to restore it when the pane closed."
|
||||
_lt "Re-probing terminal state after the fix for comparison:"
|
||||
term-probe report 2>&1 | tee -a $logfile
|
||||
_lt ""
|
||||
_lt "-> File this log against zellij (or ghostty, if a separate window also"
|
||||
_lt " lagged). The before/after probe diff pinpoints the exact stuck mode."
|
||||
else if test "$ans_keylog_instant" = y; and test "$ans_keylog_plain" = y
|
||||
_lt "Raw input reaches this pane instantly as plain bytes, and no terminal"
|
||||
_lt "state reset helps: the lag lives INSIDE this fish process (reader state)."
|
||||
_lt "Confirm now: run 'exec fish' — if that cures it, it is fish-internal."
|
||||
_lt ""
|
||||
_lt "-> To catch it in the act, run your next long-lived shell as:"
|
||||
_lt " FISH_DEBUG='reader,term-support' FISH_DEBUG_OUTPUT=$logdir/fish-debug.log fish"
|
||||
_lt " then re-run lag-triage when it recurs and file both logs to fish-shell."
|
||||
else
|
||||
_lt "Keystrokes were delayed or arrived as escape sequences BEFORE fish saw"
|
||||
_lt "them: the problem is in Zellij (client stdin parser / server) or Ghostty."
|
||||
_lt " new pane also laggy: $ans_scope_pane (y -> session-wide, not this pane)"
|
||||
_lt " separate window laggy: $ans_scope_window (y -> Ghostty itself)"
|
||||
_lt "-> File this log against zellij; include the keylog byte capture."
|
||||
end
|
||||
_lt ""
|
||||
_lt "Full log: $logfile"
|
||||
|
||||
functions -e _lt _lt_ask
|
||||
@@ -0,0 +1,165 @@
|
||||
"""Probe the terminal state of the current pane, below the shell.
|
||||
|
||||
Modes:
|
||||
report - query kitty-keyboard flags, modifyOtherKeys, and DEC private
|
||||
modes directly on /dev/tty, reporting each reply (or lack of
|
||||
one) and its round-trip latency. Answers may come from Zellij
|
||||
(pane state) or be relayed from Ghostty (window state).
|
||||
keylog - raw-mode keystroke capture: prints the exact bytes and
|
||||
inter-key latency for every keypress, bypassing the shell's
|
||||
input machinery entirely. Press q to finish.
|
||||
|
||||
Used by the `lag-triage` fish function to pin down which layer
|
||||
(fish / zellij / ghostty) is holding stuck state when typing lags
|
||||
after a TUI exits.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import sys
|
||||
import termios
|
||||
import time
|
||||
import tty
|
||||
|
||||
# DECRQM reply values
|
||||
DECRQM_VALUES = {
|
||||
"0": "not recognized",
|
||||
"1": "SET",
|
||||
"2": "reset",
|
||||
"3": "permanently set",
|
||||
"4": "permanently reset",
|
||||
}
|
||||
|
||||
DEC_MODES = [
|
||||
(1, "application cursor keys (DECCKM)"),
|
||||
(25, "cursor visible"),
|
||||
(1000, "mouse click reporting"),
|
||||
(1002, "mouse drag reporting"),
|
||||
(1003, "mouse all-motion reporting"),
|
||||
(1004, "focus reporting"),
|
||||
(1006, "SGR mouse encoding"),
|
||||
(1049, "alternate screen"),
|
||||
(2004, "bracketed paste"),
|
||||
(2026, "synchronized output"),
|
||||
(2031, "color theme reporting"),
|
||||
]
|
||||
|
||||
QUERIES = [
|
||||
("kitty keyboard flags (\\e[?u)", b"\x1b[?u", rb"\x1b\[\?(\d+)u", None),
|
||||
("modifyOtherKeys (XTQMODKEYS)", b"\x1b[?4m", rb"\x1b\[>4;(\d+)m", None),
|
||||
("background color (OSC 11)", b"\x1b]11;?\x1b\\", rb"\x1b\]11;([^\x07\x1b]+)", None),
|
||||
] + [
|
||||
(
|
||||
f"DEC mode {num} — {desc}",
|
||||
b"\x1b[?%d$p" % num,
|
||||
rb"\x1b\[\?%d;(\d+)\$y" % num,
|
||||
DECRQM_VALUES,
|
||||
)
|
||||
for num, desc in DEC_MODES
|
||||
]
|
||||
|
||||
|
||||
def read_for(fd, seconds):
|
||||
buf = b""
|
||||
end = time.monotonic() + seconds
|
||||
while True:
|
||||
remaining = end - time.monotonic()
|
||||
if remaining <= 0:
|
||||
break
|
||||
r, _, _ = select.select([fd], [], [], remaining)
|
||||
if not r:
|
||||
break
|
||||
buf += os.read(fd, 4096)
|
||||
return buf
|
||||
|
||||
|
||||
def report(fd):
|
||||
lines = []
|
||||
raw_dump = b""
|
||||
for label, query, pattern, value_names in QUERIES:
|
||||
raw_dump += read_for(fd, 0.02) # drain stragglers
|
||||
start = time.monotonic()
|
||||
os.write(fd, query)
|
||||
buf = b""
|
||||
match = None
|
||||
deadline = time.monotonic() + 0.35
|
||||
while time.monotonic() < deadline:
|
||||
buf += read_for(fd, 0.05)
|
||||
match = re.search(pattern, buf)
|
||||
if match:
|
||||
break
|
||||
raw_dump += buf
|
||||
if match:
|
||||
latency = (time.monotonic() - start) * 1000
|
||||
value = match.group(1).decode("ascii", "replace")
|
||||
if value_names:
|
||||
value = f"{value} ({value_names.get(value, '?')})"
|
||||
lines.append(f" {label:45s} = {value:24s} [{latency:6.1f} ms]")
|
||||
else:
|
||||
lines.append(f" {label:45s} = (no reply)")
|
||||
|
||||
# DA1 as a fence: every terminal answers it, so its round-trip time
|
||||
# measures the whole input path (ghostty -> zellij -> pane -> here).
|
||||
start = time.monotonic()
|
||||
os.write(fd, b"\x1b[c")
|
||||
buf = b""
|
||||
match = None
|
||||
deadline = time.monotonic() + 2.0
|
||||
while time.monotonic() < deadline:
|
||||
buf += read_for(fd, 0.05)
|
||||
match = re.search(rb"\x1b\[\?([0-9;]*)c", buf)
|
||||
if match:
|
||||
break
|
||||
raw_dump += buf
|
||||
if match:
|
||||
latency = (time.monotonic() - start) * 1000
|
||||
lines.append(
|
||||
f" {'device attributes (DA1) round-trip':45s} = "
|
||||
f"{match.group(1).decode():24s} [{latency:6.1f} ms]"
|
||||
)
|
||||
else:
|
||||
lines.append(f" {'device attributes (DA1) round-trip':45s} = (NO REPLY in 2s!)")
|
||||
lines.append(f" raw bytes received: {raw_dump!r}")
|
||||
return lines
|
||||
|
||||
|
||||
def keylog(fd):
|
||||
sys.stdout.write("keylog: capturing raw bytes from the tty. Press q to finish.\r\n")
|
||||
sys.stdout.flush()
|
||||
last = time.monotonic()
|
||||
while True:
|
||||
select.select([fd], [], [], None)
|
||||
data = os.read(fd, 4096)
|
||||
now = time.monotonic()
|
||||
delta_ms = (now - last) * 1000
|
||||
last = now
|
||||
sys.stdout.write(f" +{delta_ms:8.1f} ms {data!r} hex={data.hex(' ')}\r\n")
|
||||
sys.stdout.flush()
|
||||
if data in (b"q", b"\x03", b"\x04"):
|
||||
break
|
||||
|
||||
|
||||
def main():
|
||||
mode = sys.argv[1] if len(sys.argv) > 1 else "report"
|
||||
fd = os.open("/dev/tty", os.O_RDWR)
|
||||
old = termios.tcgetattr(fd)
|
||||
lines = None
|
||||
try:
|
||||
tty.setraw(fd)
|
||||
if mode == "report":
|
||||
lines = report(fd)
|
||||
elif mode == "keylog":
|
||||
keylog(fd)
|
||||
else:
|
||||
raise SystemExit(f"unknown mode: {mode}")
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
os.close(fd)
|
||||
if lines:
|
||||
print("terminal state as seen from this pane:")
|
||||
print("\n".join(lines))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,15 @@
|
||||
# One-shot reset of terminal state a TUI may have left behind (kitty keyboard
|
||||
# flags, modifyOtherKeys, application keypad/cursor, mouse/focus reporting,
|
||||
# alternate screen, termios). fish re-enables the modes it wants at the next
|
||||
# prompt, so this is safe to run any time.
|
||||
#
|
||||
# Diagnostic value: if this cures the lag, the stuck state was below fish
|
||||
# (run lag-triage next time to find which mode). If only `exec fish` cures
|
||||
# it, the lag is inside the fish process itself.
|
||||
printf '\e[<9u\e[=0;1u'
|
||||
printf '\e[>4;0m'
|
||||
printf '\e>\e[?1l'
|
||||
printf '\e[?1000l\e[?1001l\e[?1002l\e[?1003l\e[?1005l\e[?1006l\e[?1015l\e[?1016l\e[?1004l\e[?2026l'
|
||||
printf '\e[?1049l'
|
||||
stty sane
|
||||
echo "terminal state reset — if typing still lags, run lag-triage (before exec fish!)"
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic repro of permanent fish reader degradation (fish 4.8.1).
|
||||
|
||||
This is evidence for an upstream fish-shell report, and the proof behind the
|
||||
fish-no-query-term wrapper in presets/programs/zellij.nix. Not installed by
|
||||
the nix module; run directly: python3 upstream_repro.py [queryterm|noqueryterm]
|
||||
|
||||
Finding: with the query-term feature enabled (latched from the startup env,
|
||||
which is fish's default), fish sends OSC 11 + CPR (\e[6n) + DA1 (\e[0c)
|
||||
after every external command and waits for the replies. If the terminal
|
||||
fails to reply during ONE such cycle -- even though it answered every query
|
||||
before and answers every query after -- that fish process's interactive
|
||||
reader is PERMANENTLY degraded: keystrokes are no longer echoed (>3s each,
|
||||
never recovers). In production this happens when zellij drops/mis-relays a
|
||||
reply during TUI teardown or heavy output (cf. zellij-org/zellij#5158), and
|
||||
it presents as permanent typing lag cured only by replacing the process.
|
||||
With fish_features=no-query-term in the startup environment, the same
|
||||
sequence has zero effect (~35ms echo throughout).
|
||||
|
||||
Phases:
|
||||
A. terminal answers all queries -> echo ~35ms (both variants)
|
||||
B. replies dropped for one command -> queryterm: echo dead, permanently
|
||||
C. replies restored, another command -> queryterm: still dead
|
||||
"""
|
||||
import os, pty, re, select, subprocess, sys, time, fcntl, termios
|
||||
|
||||
VARIANT = sys.argv[1] if len(sys.argv) > 1 else "queryterm"
|
||||
|
||||
env = dict(os.environ)
|
||||
env["TERM"] = "xterm-ghostty"
|
||||
env["ZELLIJ"] = "0"
|
||||
env["ZELLIJ_SESSION_NAME"] = "repro"
|
||||
env["FISH_DEBUG"] = "term-support"
|
||||
env["FISH_DEBUG_OUTPUT"] = f"/tmp/fish-lagrepro-{VARIANT}.log"
|
||||
env.pop("fish_features", None)
|
||||
if VARIANT == "noqueryterm":
|
||||
env["fish_features"] = "no-query-term"
|
||||
|
||||
master, slave = pty.openpty()
|
||||
# give it a size
|
||||
fcntl.ioctl(master, termios.TIOCSWINSZ, b"\x00\x28\x00\x78\x00\x00\x00\x00")
|
||||
proc = subprocess.Popen(
|
||||
["fish", "-i", "--no-config"],
|
||||
stdin=slave, stdout=slave, stderr=slave, env=env,
|
||||
preexec_fn=lambda: (os.setsid(), fcntl.ioctl(0, termios.TIOCSCTTY, 0)),
|
||||
close_fds=True,
|
||||
)
|
||||
os.close(slave)
|
||||
|
||||
RESPOND = True
|
||||
transcript = []
|
||||
|
||||
def respond(data):
|
||||
"""Answer terminal queries the way a well-behaved terminal would."""
|
||||
out = b""
|
||||
for m in re.finditer(rb"\x1b\[6n", data):
|
||||
out += b"\x1b[40;1R" # CPR
|
||||
for m in re.finditer(rb"\x1b\[0?c", data):
|
||||
out += b"\x1b[?62;22c" # DA1
|
||||
for m in re.finditer(rb"\x1b\[\?u", data):
|
||||
out += b"\x1b[?0u" # kitty flags
|
||||
for m in re.finditer(rb"\x1b\]11;\?", data):
|
||||
out += b"\x1b]11;rgb:2828/2828/2828\x1b\\" # OSC 11
|
||||
for m in re.finditer(rb"\x1b\[>0?q", data):
|
||||
out += b"\x1bP>|ghostty 1.3.1\x1b\\" # XTVERSION
|
||||
for m in re.finditer(rb"\x1bP\+q[0-9a-fA-F;]+\x1b\\", data):
|
||||
out += b"\x1bP0+r\x1b\\" # XTGETTCAP: not found
|
||||
for m in re.finditer(rb"\x1b\[\?(\d+)\$p", data):
|
||||
out += b"\x1b[?%s;2$y" % m.group(1) # DECRQM: reset
|
||||
return out
|
||||
|
||||
def pump(timeout):
|
||||
"""Read fish output for `timeout` seconds, answering queries if RESPOND."""
|
||||
buf = b""
|
||||
end = time.monotonic() + timeout
|
||||
while time.monotonic() < end:
|
||||
r, _, _ = select.select([master], [], [], 0.03)
|
||||
if r:
|
||||
try:
|
||||
data = os.read(master, 65536)
|
||||
except OSError:
|
||||
return buf
|
||||
buf += data
|
||||
transcript.append(data)
|
||||
if RESPOND:
|
||||
reply = respond(data)
|
||||
if reply:
|
||||
os.write(master, reply)
|
||||
return buf
|
||||
|
||||
def send(s):
|
||||
os.write(master, s if isinstance(s, bytes) else s.encode())
|
||||
|
||||
def measure_echo(chars, settle=0.1):
|
||||
"""Send chars one at a time; measure time until each is echoed."""
|
||||
results = []
|
||||
for ch in chars:
|
||||
pump(settle)
|
||||
t0 = time.monotonic()
|
||||
send(ch)
|
||||
deadline = time.monotonic() + 3.0
|
||||
latency = None
|
||||
buf = b""
|
||||
while time.monotonic() < deadline:
|
||||
buf += pump(0.02)
|
||||
if ch.encode() in buf:
|
||||
latency = (time.monotonic() - t0) * 1000
|
||||
break
|
||||
results.append((ch, latency))
|
||||
return results
|
||||
|
||||
print(f"=== variant: {VARIANT} ===")
|
||||
pump(1.2) # startup, queries answered
|
||||
|
||||
send("echo warmup\r")
|
||||
pump(0.8)
|
||||
|
||||
print("phase A: terminal responsive, echo latency per key:")
|
||||
for ch, ms in measure_echo("abcde"):
|
||||
print(f" {ch}: {ms:.0f} ms" if ms else f" {ch}: NO ECHO in 3s")
|
||||
send("\x15") # ctrl-u clear line
|
||||
pump(0.3)
|
||||
|
||||
# Run external command, then STOP answering queries (simulate lost relay)
|
||||
send("sh -c true\r")
|
||||
time.sleep(0.05)
|
||||
RESPOND = False
|
||||
pump(1.0)
|
||||
|
||||
print("phase B: after external command with query replies DROPPED:")
|
||||
for ch, ms in measure_echo("fghij"):
|
||||
print(f" {ch}: {ms:.0f} ms" if ms else f" {ch}: NO ECHO in 3s")
|
||||
send("\x15")
|
||||
pump(0.3)
|
||||
|
||||
# Does it persist across further commands, with responses restored?
|
||||
RESPOND = True
|
||||
send("sh -c true\r")
|
||||
pump(1.0)
|
||||
print("phase C: responses restored, after another external command:")
|
||||
for ch, ms in measure_echo("klmno"):
|
||||
print(f" {ch}: {ms:.0f} ms" if ms else f" {ch}: NO ECHO in 3s")
|
||||
|
||||
send("\x15")
|
||||
pump(0.2)
|
||||
send("exit\r")
|
||||
pump(0.5)
|
||||
try:
|
||||
proc.wait(timeout=3)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
@@ -26,7 +26,7 @@ let
|
||||
-E pr=5000/prompt \
|
||||
-H "${ldap_scheme}://''${LDAP_HOST}:${builtins.toString ldap_port}" \
|
||||
-D "${pkgs.lib.toUpper magic_prefix}2\\${pkgs.lib.toLower config.home.username}" \
|
||||
-w "$(${pkgs._1password-cli}/bin/op item get T2 --fields label=password --reveal)" \
|
||||
-w "$(/usr/local/bin/op item get T2 --fields label=password --reveal)" \
|
||||
-b "dc=''${LDAP_HOST//./,dc=}" \
|
||||
-s "sub" -x "(cn=''${SEARCH_FILTER})" \
|
||||
| ${jq_parse}/bin/ljq
|
||||
|
||||
@@ -38,13 +38,13 @@ in
|
||||
);
|
||||
|
||||
# Use Neovim as the editor for git commit messages
|
||||
programs.git.extraConfig.core.editor = "${lib.getExe cfg.package}";
|
||||
programs.git.settings.core.editor = "${lib.getExe cfg.package}";
|
||||
programs.jujutsu.settings.ui.editor = "${lib.getExe cfg.package}";
|
||||
|
||||
# Set Neovim as the default app for text editing and manual pages
|
||||
home.sessionVariables = {
|
||||
EDITOR = "${lib.getExe cfg.package}";
|
||||
MANPAGER = "${lib.getExe cfg.package} +Man!";
|
||||
# MANPAGER = "${lib.getExe cfg.package} +Man!";
|
||||
};
|
||||
|
||||
# Create quick aliases for launching Neovim
|
||||
@@ -63,10 +63,10 @@ in
|
||||
# Create a desktop option for launching Neovim from a file manager
|
||||
# (Requires launching the terminal and then executing Neovim)
|
||||
xdg.desktopEntries.nvim =
|
||||
lib.mkIf (pkgs.stdenv.isLinux && config.nmasur.presets.services.i3.enable)
|
||||
lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && config.nmasur.presets.services.i3.enable)
|
||||
{
|
||||
name = "Neovim wrapper";
|
||||
exec = "${lib.getExe config.nmasur.presets.services.i3.terminal} nvim %F"; # TODO: change to generic
|
||||
exec = ''${lib.getExe config.nmasur.presets.services.i3.terminal} --command="nvim %F"''; # TODO: change to generic
|
||||
mimeType = [
|
||||
"text/plain"
|
||||
"text/markdown"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
# pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
@@ -23,17 +23,18 @@ in
|
||||
};
|
||||
|
||||
# Create nix-index if doesn't exist
|
||||
home.activation.createNixIndex =
|
||||
let
|
||||
cacheDir = "${config.xdg.cacheHome}/nix-index";
|
||||
in
|
||||
lib.mkIf config.programs.nix-index.enable (
|
||||
config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||
if [ ! -d ${cacheDir} ]; then
|
||||
run ${pkgs.nix-index}/bin/nix-index -f ${pkgs.path}
|
||||
fi
|
||||
''
|
||||
);
|
||||
# Should not be necessary with the nix-index-database flake
|
||||
# home.activation.createNixIndex =
|
||||
# let
|
||||
# cacheDir = "${config.xdg.cacheHome}/nix-index";
|
||||
# in
|
||||
# lib.mkIf config.programs.nix-index.enable (
|
||||
# config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||
# if [ ! -d ${cacheDir} ]; then
|
||||
# run ${pkgs.nix-index}/bin/nix-index -f ${pkgs.path}
|
||||
# fi
|
||||
# ''
|
||||
# );
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ in
|
||||
description = "Nixpkgs tools for macOS";
|
||||
default =
|
||||
config.nmasur.presets.programs.nixpkgs.enable
|
||||
&& pkgs.stdenv.isDarwin
|
||||
&& pkgs.stdenv.hostPlatform.isDarwin
|
||||
&& config.nmasur.presets.programs.dotfiles.enable;
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,18 @@ in
|
||||
doas nixos-rebuild switch --flake ${config.nmasur.presets.programs.dotfiles.path}
|
||||
'';
|
||||
};
|
||||
rebuildNixosAndPause = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.writeShellScriptBin "rebuild-nixos-pause" ''
|
||||
${lib.getExe cfg.commands.rebuildNixos} || read
|
||||
'';
|
||||
};
|
||||
rebuildHomeAndPause = lib.mkOption {
|
||||
type = lib.types.package;
|
||||
default = pkgs.writeShellScriptBin "rebuild-home-pause" ''
|
||||
${lib.getExe cfg.commands.rebuildHome} || read
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -13,12 +13,23 @@ in
|
||||
|
||||
options.nmasur.presets.programs.noti.enable = lib.mkEnableOption "Noti CLI notifications";
|
||||
|
||||
config = lib.mkIf (cfg.enable && (pkgs.stdenv.isDarwin || config.services.dunst.enable)) {
|
||||
config = lib.mkIf (cfg.enable && (pkgs.stdenv.hostPlatform.isDarwin || config.services.dunst.enable)) {
|
||||
home.packages = [ pkgs.noti ];
|
||||
programs.fish.shellAbbrs = {
|
||||
# Add noti for ghpr in Darwin
|
||||
ghpr = lib.mkForce "gh pr create && sleep 3 && noti gh run watch";
|
||||
grw = lib.mkForce "noti gh run watch";
|
||||
programs.fish = {
|
||||
shellAbbrs = {
|
||||
# Add noti for ghpr in Darwin
|
||||
ghpr = lib.mkForce "gh pr create && sleep 3 && noti gh run watch";
|
||||
grw = lib.mkForce "noti gh run watch";
|
||||
};
|
||||
functions = {
|
||||
gh-run = {
|
||||
body =
|
||||
lib.mkForce # fish
|
||||
''
|
||||
${lib.getExe pkgs.zellij} action new-pane --start-suspended -- noti gh run watch
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ in
|
||||
|
||||
# Broken on 2023-12-11
|
||||
# https://forum.obsidian.md/t/electron-25-is-now-eol-please-upgrade-to-a-newer-version/72878/8
|
||||
# insecurePackages = [ "electron-25.9.0" ];
|
||||
# allowInsecurePackages = [ "electron-36.9.5" ];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ in
|
||||
.terraform/
|
||||
.target/
|
||||
/Library/
|
||||
.jj/
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,6 +15,8 @@ in
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.packages = [ pkgs.jj-starship ];
|
||||
|
||||
programs.starship = {
|
||||
enable = true;
|
||||
enableFishIntegration = true;
|
||||
@@ -27,6 +29,7 @@ in
|
||||
"$git_branch"
|
||||
"$git_commit"
|
||||
"$git_status"
|
||||
"\${custom.jj}"
|
||||
"$hostname"
|
||||
"$cmd_duration"
|
||||
"$character"
|
||||
@@ -38,23 +41,37 @@ in
|
||||
vicmd_symbol = "[❮](bold green)";
|
||||
};
|
||||
cmd_duration = {
|
||||
min_time = 5000;
|
||||
show_notifications = if pkgs.stdenv.isLinux then false else true;
|
||||
min_time = 5001;
|
||||
show_notifications = if pkgs.stdenv.hostPlatform.isLinux then false else true;
|
||||
min_time_to_notify = 30000;
|
||||
format = "[$duration]($style) ";
|
||||
};
|
||||
custom = {
|
||||
jj = {
|
||||
when = "jj-starship detect";
|
||||
shell = [
|
||||
"jj-starship"
|
||||
"--no-jj-prefix"
|
||||
"--no-git-prefix"
|
||||
];
|
||||
format = "$output ";
|
||||
};
|
||||
};
|
||||
directory = {
|
||||
truncate_to_repo = true;
|
||||
truncation_length = 100;
|
||||
};
|
||||
git_branch = {
|
||||
disabled = true;
|
||||
format = "[$symbol$branch]($style)";
|
||||
};
|
||||
git_commit = {
|
||||
disabled = true;
|
||||
format = "( @ [$hash]($style) )";
|
||||
only_detached = false;
|
||||
};
|
||||
git_status = {
|
||||
disabled = true;
|
||||
format = "([$all_status$ahead_behind]($style) )";
|
||||
conflicted = "=";
|
||||
ahead = "⇡";
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
-----BEGIN AGE ENCRYPTED FILE-----
|
||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IE1nSGFPdyBTYTRy
|
||||
elYzTGpSNDQ3UEcwTXlVeGJleUJmWWhaeDdDQTFRcGpmNlNrQlNVCjA5L2JrS3Vx
|
||||
Q0cyRkk5dTBLOHJXa0xJSG9MTDFnNjV1M0F5L3F5RUlVbW8KLT4gc3NoLWVkMjU1
|
||||
MTkgWXlTVU1RIG5vNm1Xem9lN2pkS25WRi9xSlpZUjhuYmdUVDUvc2o4M0xqYURR
|
||||
UmY0VGcKWVVQc2wyV0Jqbk9JR3N4bW5HOXFTZHpCa25EMC85eThQY05MdHdaeXZy
|
||||
VQotPiBzc2gtZWQyNTUxOSBuanZYNUEgWXRVa3c4STZ3WmFaNThSdE1QdVpiMVR1
|
||||
cm5hYXJsckZiRGtXLzN5RzJEawpkVHBscFd2c0R1SGxnZ3lKUnNnMEZtTUxoQlB4
|
||||
dVBEbTkvUzBJSVRiV1hBCi0+IHNzaC1lZDI1NTE5IENxSU9VQSAvQjhVam1heHNU
|
||||
elVrVGtvaUx1elFCeTdNTkRnN3c5NEc5MWg4dDU3NUhzCm5sUlhHclJrNldnVDhF
|
||||
MTV2cGd3ZFhFdm1rM2ExWVFXbkNJYWlWY0VnUmsKLT4gc3NoLWVkMjU1MTkgejFP
|
||||
Y1p3IGc1QUdkZEp0Z0xEekFjcHd4WVFVam9BZTBEQm9NR3QzQmxNS09VVXpHV2sK
|
||||
c2tYSElVK2prRlF3VlFqKzlVUFRHUWU3TmFXcEdsV2FKWVhKT3pWZkxVNAotLS0g
|
||||
c1cxdk5sL1c3dDZuVGp5VWJrTlBGZTByNjRxMGxTdHd0NFNHV1pyN2k5Ywr7SW9q
|
||||
/FaTTUHB5QiCihA+385sNogq7Q1RvgT2Dwn9NdmMRd/ObESbokJXVSiDDEt6d39s
|
||||
D/uoDY20p3PCk4julNn1
|
||||
-----END AGE ENCRYPTED FILE-----
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (config.nmasur.settings) hostnames;
|
||||
cfg = config.nmasur.presets.programs.thunderbird;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.programs.thunderbird = {
|
||||
enable = lib.mkEnableOption "Thunderbird email client";
|
||||
calendar = {
|
||||
username = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Username for the calendar service backend";
|
||||
default = config.nmasur.settings.username;
|
||||
};
|
||||
passwordCommand = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Password for the calendar service backend";
|
||||
default = config.accounts.email.accounts.home.passwordCommand;
|
||||
};
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for the calendar service backend";
|
||||
default = hostnames.content;
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "URL for the calendar service backend";
|
||||
default = "https://${cfg.calendar.hostname}/remote.php/dav";
|
||||
};
|
||||
};
|
||||
tasks = {
|
||||
username = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Username for the tasks service backend";
|
||||
default = config.nmasur.settings.username;
|
||||
};
|
||||
passwordCommand = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Password for the tasks service backend";
|
||||
default = "${lib.getExe pkgs.age} --decrypt --identity ~/.ssh/id_ed25519 ${pkgs.writeText "taskspass.age" (builtins.readFile ./taskspass.age)}";
|
||||
};
|
||||
hostname = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "Hostname for the tasks service backend";
|
||||
default = hostnames.content;
|
||||
};
|
||||
url = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
description = "URL for the tasks service backend";
|
||||
default = "https://${cfg.tasks.hostname}/remote.php/dav";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
programs.thunderbird = {
|
||||
enable = true;
|
||||
profiles.default = {
|
||||
isDefault = true;
|
||||
};
|
||||
};
|
||||
accounts.email.accounts.home.thunderbird = {
|
||||
enable = true;
|
||||
profiles = [ "default" ];
|
||||
};
|
||||
accounts.calendar.basePath = "other/calendars"; # Where to save calendars in ~ directory
|
||||
# accounts.calendar.accounts.home = {
|
||||
# local.type = "filesystem";
|
||||
# primary = true;
|
||||
# remote = {
|
||||
# passwordCommand = [ cfg.calendar.passwordCommand ];
|
||||
# type = "caldav";
|
||||
# url = cfg.calendar.url;
|
||||
# userName = cfg.calendar.username;
|
||||
# };
|
||||
# thunderbird = {
|
||||
# enable = true;
|
||||
# profiles = [ "default" ];
|
||||
# };
|
||||
# };
|
||||
# accounts.calendar.accounts.tasks = {
|
||||
# local.type = "filesystem";
|
||||
# primary = false;
|
||||
# remote = {
|
||||
# passwordCommand = [ cfg.tasks.passwordCommand ];
|
||||
# type = "caldav";
|
||||
# url = cfg.tasks.url;
|
||||
# userName = cfg.tasks.username;
|
||||
# };
|
||||
# thunderbird = {
|
||||
# enable = true;
|
||||
# profiles = [ "default" ];
|
||||
# };
|
||||
# };
|
||||
|
||||
};
|
||||
}
|
||||
@@ -105,7 +105,7 @@ in
|
||||
}
|
||||
|
||||
config.font = wezterm.font('${cfg.font}', { weight = 'Bold'})
|
||||
config.font_size = ${if pkgs.stdenv.isLinux then "14.0" else "18.0"}
|
||||
config.font_size = ${if pkgs.stdenv.hostPlatform.isLinux then "14.0" else "18.0"}
|
||||
|
||||
-- Fix color blocks instead of text
|
||||
config.front_end = "WebGpu"
|
||||
@@ -114,7 +114,7 @@ in
|
||||
config.hide_tab_bar_if_only_one_tab = true
|
||||
config.window_frame = {
|
||||
font = wezterm.font('${cfg.font}', { weight = 'Bold'}),
|
||||
font_size = ${if pkgs.stdenv.isLinux then "12.0" else "16.0"},
|
||||
font_size = ${if pkgs.stdenv.hostPlatform.isLinux then "12.0" else "16.0"},
|
||||
}
|
||||
|
||||
config.colors = {
|
||||
@@ -191,7 +191,7 @@ in
|
||||
-- super-t open new tab in new dir
|
||||
{
|
||||
key = 't',
|
||||
mods = ${if pkgs.stdenv.isDarwin then "'SUPER'" else "'ALT'"},
|
||||
mods = ${if pkgs.stdenv.hostPlatform.isDarwin then "'SUPER'" else "'ALT'"},
|
||||
action = wezterm.action.SpawnCommandInNewTab {
|
||||
cwd = wezterm.home_dir,
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ in
|
||||
config = lib.mkIf cfg.enable {
|
||||
programs.zed-editor = {
|
||||
enable = true;
|
||||
package = pkgs.stable.zed-editor;
|
||||
|
||||
extensions = [
|
||||
"nix"
|
||||
|
||||
@@ -6,15 +6,32 @@
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (config.nmasur.settings) username;
|
||||
cfg = config.nmasur.presets.programs.zellij;
|
||||
|
||||
# fish latches feature flags from its startup ENVIRONMENT before config.fish
|
||||
# runs, so the `set -gx fish_features no-query-term` in config.fish only
|
||||
# protects CHILD fish processes — which is why subshells/exec fish were always
|
||||
# immune to the post-TUI typing lag while zellij-spawned pane shells were not.
|
||||
# With query-term latched on, fish sends OSC 11 + CPR + DA1 queries after
|
||||
# every command and waits for replies; if zellij fails to relay even one
|
||||
# reply (a race during TUI teardown or heavy output), that fish process's
|
||||
# reader is PERMANENTLY degraded — reproduced deterministically in a PTY
|
||||
# harness on fish 4.8.1 (see docs/CHANGELOG.md 2026-08-29 and
|
||||
# presets/programs/lag-triage/upstream_repro.py). Spawning fish with the
|
||||
# variable already exported makes every pane shell immune.
|
||||
fish-no-query-term = pkgs.writeShellScriptBin "fish-no-query-term" ''
|
||||
export fish_features=no-query-term
|
||||
exec ${lib.getExe pkgs.fish} "$@"
|
||||
'';
|
||||
|
||||
zellij-switch-to-last = pkgs.writeShellScriptBin "zellij-switch-to-last" ''
|
||||
TARGET_SESSION=$(cat ~/.local/state/zellij-last-session)
|
||||
if [ -z "$TARGET_SESSION" ]; then
|
||||
return 1
|
||||
fi
|
||||
echo "$ZELLIJ_SESSION_NAME" > ~/.local/state/zellij-last-session
|
||||
zellij pipe --plugin file:$(which zellij-switch.wasm) -- "--session $TARGET_SESSION"
|
||||
${lib.getExe pkgs.zellij} action switch-session $TARGET_SESSION
|
||||
'';
|
||||
in
|
||||
|
||||
@@ -24,8 +41,6 @@ in
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.packages = [ pkgs.zellij-switch ];
|
||||
|
||||
programs.fish = {
|
||||
shellAbbrs.z = "zellij";
|
||||
functions = {
|
||||
@@ -33,26 +48,63 @@ in
|
||||
# description = "Open a session in Zellij";
|
||||
body = # fish
|
||||
''
|
||||
set TARGET_DIR $(zoxide query --interactive)
|
||||
set TARGET_DIR $( \
|
||||
zoxide query --list --score | \
|
||||
fzf --filter="$query" --no-sort | \
|
||||
fzf \
|
||||
--prompt="zoxide > " \
|
||||
--nth=2.. \
|
||||
--ansi \
|
||||
--height=60% \
|
||||
--info=inline \
|
||||
--border=rounded \
|
||||
--layout=reverse \
|
||||
--preview-window=down:40%:wrap \
|
||||
--preview='ls -F -C --color=always {2..}' \
|
||||
--bind 'ctrl-z:ignore,btab:up,tab:down,enter:become:echo {2..}' \
|
||||
--cycle \
|
||||
--keep-right \
|
||||
--tabstop=1 \
|
||||
)
|
||||
if test -z $TARGET_DIR
|
||||
return 0
|
||||
end
|
||||
if test "$TARGET_DIR" = $(pwd)
|
||||
return 1
|
||||
end
|
||||
# Zellij names each session's IPC socket $TMPDIR/zellij-<uid>/<ver>/<name>.
|
||||
# On macOS the socket path is capped at 103 bytes; the $TMPDIR prefix under
|
||||
# /var/folders/... eats ~79 of those, leaving only ~24 chars for the name.
|
||||
# A longer basename overflows the socket path, and because switch-session has
|
||||
# already detached from the current session by the time the new one fails to
|
||||
# bind, it takes the whole terminal down. Truncate to stay well under the limit.
|
||||
set SESSION_NAME (basename $TARGET_DIR | string sub --length 20)
|
||||
echo "$ZELLIJ_SESSION_NAME" > ~/.local/state/zellij-last-session
|
||||
zellij pipe --plugin file:$(which zellij-switch.wasm) -- "--cwd $TARGET_DIR --layout default --session $(basename $TARGET_DIR)"
|
||||
${lib.getExe pkgs.zellij} action switch-session $SESSION_NAME --cwd $TARGET_DIR --layout default
|
||||
'';
|
||||
};
|
||||
gh-run = {
|
||||
body = # fish
|
||||
''
|
||||
zellij action new-pane --start-suspended -- gh run watch
|
||||
${lib.getExe pkgs.zellij} action new-pane --start-suspended -- gh run watch
|
||||
'';
|
||||
};
|
||||
__fish_update_cwd_osc = {
|
||||
description = "Notify Zellij/terminal of CWD changes without hostname mismatch";
|
||||
body = # fish
|
||||
''
|
||||
if test "$TERM" != "dumb"
|
||||
printf \e\]7\;file://%s\a (string escape --style=url -- $PWD)
|
||||
end
|
||||
'';
|
||||
onVariable = "PWD";
|
||||
onEvent = "fish_prompt";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
programs.ghostty.settings.initial-command = lib.getExe pkgs.zellij;
|
||||
|
||||
xdg.configFile."zellij/layouts/compact-top.kdl".text = # kdl
|
||||
''
|
||||
layout {
|
||||
@@ -85,19 +137,55 @@ in
|
||||
enableFishIntegration = true;
|
||||
enableZshIntegration = true;
|
||||
attachExistingSession = true;
|
||||
exitShellOnExit = false;
|
||||
exitShellOnExit = true;
|
||||
|
||||
settings = {
|
||||
default_mode = "locked";
|
||||
# Spawn fish directly instead of trusting $SHELL, which inherits the
|
||||
# macOS login shell. On darwin that login shell is no longer managed by
|
||||
# nix-darwin, so $SHELL can point at a stale /run/current-system path.
|
||||
# Wrapped to export fish_features=no-query-term BEFORE fish starts —
|
||||
# see the fish-no-query-term comment above for why this must happen in
|
||||
# the environment rather than in config.fish.
|
||||
default_shell = lib.getExe fish-no-query-term;
|
||||
# default_layout = "compact-top";
|
||||
# Remove border
|
||||
pane_frames = false;
|
||||
# Ghostty + Zellij have several open upstream bugs where the Kitty
|
||||
# keyboard protocol's "enhancement" flags get left in a stuck/elevated
|
||||
# state after a full-screen TUI exits (e.g. zellij-org/zellij#3887,
|
||||
# #3723, #4178), causing every subsequent keystroke to be sent as a
|
||||
# CSI-u sequence that fish has to wait out an escape-timeout to
|
||||
# disambiguate. This shows up as typing lag that builds up the longer
|
||||
# you were inside the TUI, until the pane's protocol state resets.
|
||||
# Disabling it entirely avoids the whole bug class.
|
||||
support_kitty_keyboard_protocol = false;
|
||||
# Scrollback
|
||||
scrollback_editor = config.home.sessionVariables.EDITOR;
|
||||
|
||||
show_startup_tips = false;
|
||||
|
||||
keybinds = {
|
||||
locked = lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && !config.programs.ghostty.enable) {
|
||||
# For servers that I SSH into, use c-a-G to unlock
|
||||
"bind \"Ctrl Alt G\"" =
|
||||
lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && !config.programs.ghostty.enable)
|
||||
{
|
||||
SwitchToMode = {
|
||||
_args = [ "normal" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
normal = lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && !config.programs.ghostty.enable) {
|
||||
# For servers that I SSH into, use c-a-G to lock
|
||||
"bind \"Ctrl Alt G\"" =
|
||||
lib.mkIf (pkgs.stdenv.hostPlatform.isLinux && !config.programs.ghostty.enable)
|
||||
{
|
||||
SwitchToMode = {
|
||||
_args = [ "locked" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
session = {
|
||||
"bind \"w\"" = {
|
||||
LaunchOrFocusPlugin = {
|
||||
@@ -121,9 +209,15 @@ in
|
||||
shared = {
|
||||
"bind \"Alt Shift s\"" = {
|
||||
Run = {
|
||||
_args = [
|
||||
(lib.getExe zellij-switch-to-last)
|
||||
];
|
||||
_args =
|
||||
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||
[
|
||||
# "env"
|
||||
# "PATH=/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin:/usr/bin:/bin"
|
||||
(lib.getExe zellij-switch-to-last)
|
||||
]
|
||||
else
|
||||
[ (lib.getExe zellij-switch-to-last) ];
|
||||
close_on_exit = true;
|
||||
};
|
||||
};
|
||||
@@ -155,11 +249,13 @@ in
|
||||
};
|
||||
"bind \"Alt Shift w\"" = {
|
||||
Run = {
|
||||
_args = [
|
||||
(lib.getExe pkgs.gh)
|
||||
"run"
|
||||
"watch"
|
||||
];
|
||||
_args =
|
||||
(if config.nmasur.presets.programs.noti.enable then [ (lib.getExe pkgs.noti) ] else [ ])
|
||||
++ [
|
||||
(lib.getExe pkgs.gh)
|
||||
"run"
|
||||
"watch"
|
||||
];
|
||||
# direction = "Right";
|
||||
# close_on_exit = false;
|
||||
# start_suspended = true;
|
||||
@@ -186,9 +282,28 @@ in
|
||||
};
|
||||
};
|
||||
"bind \"Alt Shift j\"" = {
|
||||
Run = {
|
||||
_args =
|
||||
if pkgs.stdenv.hostPlatform.isDarwin then
|
||||
[
|
||||
"env"
|
||||
"PATH=${config.home.homeDirectory}/.nix-profile/bin:/etc/profiles/per-user/${username}/bin:/usr/bin"
|
||||
(lib.getExe pkgs.jjui)
|
||||
]
|
||||
else
|
||||
[ (lib.getExe pkgs.jjui) ];
|
||||
close_on_exit = true;
|
||||
floating = true;
|
||||
x = "1%";
|
||||
y = "1%";
|
||||
width = "99%";
|
||||
height = "99%";
|
||||
};
|
||||
};
|
||||
"bind \"Alt Shift y\"" = {
|
||||
Run = {
|
||||
_args = [
|
||||
(lib.getExe pkgs.lazyjj)
|
||||
(lib.getExe pkgs.yazi)
|
||||
];
|
||||
close_on_exit = true;
|
||||
floating = true;
|
||||
@@ -201,33 +316,62 @@ in
|
||||
"bind \"Super Shift ]\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Alt ]\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Alt }\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Alt Shift ]\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Alt Shift }\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Super Shift [\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Alt [\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Alt {\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Alt Shift [\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Alt Shift {\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Ctrl Tab\"" = {
|
||||
GoToNextTab = { };
|
||||
};
|
||||
"bind \"Ctrl Shift Tab\"" = {
|
||||
GoToPreviousTab = { };
|
||||
};
|
||||
"bind \"Super t\"" = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
"bind \"Super t\"" = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||
NewTab = { };
|
||||
};
|
||||
"bind \"Alt t\"" = lib.mkIf pkgs.stdenv.isLinux {
|
||||
"bind \"Alt t\"" = {
|
||||
NewTab = { };
|
||||
};
|
||||
"bind \"Super k\"" = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
"bind \"Super k\"" = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||
SwitchToMode = {
|
||||
_args = [ "scroll" ];
|
||||
};
|
||||
};
|
||||
"bind \"Super Shift e\"" = lib.mkIf pkgs.stdenv.isDarwin {
|
||||
"bind \"Alt Shift k\"" = {
|
||||
SwitchToMode = {
|
||||
_args = [ "scroll" ];
|
||||
};
|
||||
};
|
||||
"bind \"Super Shift e\"" = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||
EditScrollback = { };
|
||||
SwitchToMode = {
|
||||
_args = [ "locked" ];
|
||||
};
|
||||
};
|
||||
"bind \"Alt Shift e\"" = lib.mkIf pkgs.stdenv.isLinux {
|
||||
"bind \"Alt Shift e\"" = {
|
||||
EditScrollback = { };
|
||||
SwitchToMode = {
|
||||
_args = [ "locked" ];
|
||||
@@ -243,6 +387,16 @@ in
|
||||
_args = [ "Left" ];
|
||||
};
|
||||
};
|
||||
"bind \"Alt j\"" = {
|
||||
MoveFocus = {
|
||||
_args = [ "Down" ];
|
||||
};
|
||||
};
|
||||
"bind \"Alt k\"" = {
|
||||
MoveFocus = {
|
||||
_args = [ "Up" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.services.darwin-settings;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.services.darwin-settings.enable = lib.mkEnableOption "macOS settings";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
targets.darwin.defaults = {
|
||||
NSGlobalDomain = {
|
||||
|
||||
# Set to dark mode
|
||||
AppleInterfaceStyle = "Dark";
|
||||
|
||||
# Don't change from dark to light automatically
|
||||
# AppleInterfaceSwitchesAutomatically = false;
|
||||
|
||||
# Enable full keyboard access for all controls (e.g. enable Tab in modal dialogs)
|
||||
AppleKeyboardUIMode = 3;
|
||||
|
||||
# Expand save panel by default
|
||||
NSNavPanelExpandedStateForSaveMode = true;
|
||||
|
||||
# Expand print panel by default
|
||||
PMPrintingExpandedStateForPrint = true;
|
||||
|
||||
# Replace press-and-hold with key repeat
|
||||
ApplePressAndHoldEnabled = false;
|
||||
|
||||
# Set a fast key repeat rate
|
||||
KeyRepeat = 2;
|
||||
|
||||
# Shorten delay before key repeat begins
|
||||
InitialKeyRepeat = 12;
|
||||
|
||||
# Disable autocorrect capitalization
|
||||
NSAutomaticCapitalizationEnabled = false;
|
||||
|
||||
# Disable autocorrect smart dashes
|
||||
NSAutomaticDashSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect adding periods
|
||||
NSAutomaticPeriodSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect smart quotation marks
|
||||
NSAutomaticQuoteSubstitutionEnabled = false;
|
||||
|
||||
# Disable autocorrect spellcheck
|
||||
NSAutomaticSpellingCorrectionEnabled = false;
|
||||
};
|
||||
|
||||
"com.apple.LaunchServices" = {
|
||||
# Disable "Are you sure you want to open" dialog
|
||||
LSQuarantine = false;
|
||||
};
|
||||
|
||||
# Where to save screenshots
|
||||
"com.apple.screencapture" = {
|
||||
location = "~/Downloads";
|
||||
};
|
||||
|
||||
# Disable disk image verification
|
||||
"com.apple.frameworks.diskimages" = {
|
||||
skip-verify = true;
|
||||
skip-verify-locked = true;
|
||||
skip-verify-remote = true;
|
||||
};
|
||||
# Require password immediately after screen saver begins
|
||||
"com.apple.screensaver" = {
|
||||
askForPassword = 1;
|
||||
askForPasswordDelay = 0;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
targets.darwin.currentHostDefaults = {
|
||||
"com.apple.mouse.tapBehavior" = {
|
||||
# Disable trackpad tap to click
|
||||
Clicking = false;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.services.dock;
|
||||
|
||||
# macOS stores persistent-apps as an array of dicts, not strings.
|
||||
# nix-darwin's dock module did this transform for us; targets.darwin.defaults
|
||||
# writes values verbatim, so we build the tile dicts ourselves.
|
||||
mkDockApp = path: {
|
||||
tile-data.file-data = {
|
||||
_CFURLString = path;
|
||||
_CFURLStringType = 0;
|
||||
};
|
||||
};
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.services.dock.enable = lib.mkEnableOption "macOS Dock";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
targets.darwin.defaults = {
|
||||
"com.apple.dock" = {
|
||||
magnification = true;
|
||||
largesize = 48;
|
||||
|
||||
# Automatically show and hide the dock
|
||||
autohide = true;
|
||||
|
||||
# Add translucency in dock for hidden applications
|
||||
showhidden = true;
|
||||
|
||||
# Enable spring loading on all dock items
|
||||
enable-spring-load-actions-on-all-items = true;
|
||||
|
||||
# Highlight hover effect in dock stack grid view
|
||||
mouse-over-hilite-stack = true;
|
||||
|
||||
mineffect = "genie";
|
||||
orientation = "bottom";
|
||||
show-recents = false;
|
||||
tilesize = 44;
|
||||
|
||||
persistent-apps = map mkDockApp [
|
||||
"/Applications/1Password.app"
|
||||
"${pkgs.thunderbird}/Applications/Thunderbird.app"
|
||||
"${pkgs.slack}/Applications/Slack.app"
|
||||
"/System/Applications/Reminders.app"
|
||||
"/System/Applications/Calendar.app"
|
||||
"/Applications/Claude.app"
|
||||
"${pkgs.firefox-unwrapped}/Applications/Firefox.app"
|
||||
"/System/Applications/Messages.app"
|
||||
"/System/Applications/Preview.app"
|
||||
"/System/Applications/Mail.app"
|
||||
"/Applications/zoom.us.app"
|
||||
"${config.programs.ghostty.package}/Applications/Ghostty.app"
|
||||
# Discord comes from Homebrew on macOS: the nixpkgs bundle is
|
||||
# modified in place, breaking its notarization seal, so macOS 26+
|
||||
# refuses to launch it ("damaged"). See presets/programs/homebrew.nix.
|
||||
"/Applications/Discord.app"
|
||||
"${pkgs.obsidian}/Applications/Obsidian.app"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
# Automatically reload the Dock and Finder when Home Manager switches
|
||||
home.activation = {
|
||||
reloadMacSettings = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||
verboseEcho "Restarting macOS desktop services..."
|
||||
run /usr/bin/killall Dock Finder || true
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.services.finder;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.services.finder.enable = lib.mkEnableOption "macOS Finder options";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
targets.darwin.defaults = {
|
||||
"com.apple.finder" = {
|
||||
# Default Finder window set to column view
|
||||
FXPreferredViewStyle = "clmv";
|
||||
|
||||
# Finder search in current folder by default
|
||||
FXDefaultSearchScope = "SCcf";
|
||||
|
||||
# Disable warning when changing file extension
|
||||
FXEnableExtensionChangeWarning = false;
|
||||
|
||||
# Allow quitting of Finder application
|
||||
QuitMenuItem = true;
|
||||
|
||||
# Disable the warning before emptying the Trash
|
||||
WarnOnEmptyTrash = false;
|
||||
|
||||
};
|
||||
|
||||
NSGlobalDomain = {
|
||||
|
||||
# Save to local disk by default, not iCloud
|
||||
NSDocumentSaveNewDocumentsToCloud = false;
|
||||
|
||||
};
|
||||
|
||||
# Avoid creating .DS_Store files on network or USB volumes
|
||||
"com.apple.desktopservices" = {
|
||||
DSDontWriteNetworkStores = true;
|
||||
DSDontWriteUSBStores = true;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
--- === Hide Zoom Window ===
|
||||
-- Credit: https://news.ycombinator.com/item?id=47369091
|
||||
|
||||
local obj = {}
|
||||
obj.__index = obj
|
||||
|
||||
-- Metadata
|
||||
obj.name = "HideZoomWindow"
|
||||
obj.version = "0.1"
|
||||
|
||||
function obj:init()
|
||||
-- Hide Zoom's "share" windows so it doesn't come back on ESC keypress
|
||||
local zoomWindow = nil
|
||||
local originalFrame = nil
|
||||
|
||||
hs.hotkey.bind({"cmd", "ctrl", "alt"}, "H", function()
|
||||
print("> trying to hide zoom")
|
||||
if not zoomWindow then
|
||||
print("> looking for window")
|
||||
zoomWindow = hs.window.find("zoom share statusbar window")
|
||||
end
|
||||
|
||||
if zoomWindow then
|
||||
print("> found window")
|
||||
if originalFrame then
|
||||
print("> restoring")
|
||||
zoomWindow:setFrame(originalFrame)
|
||||
originalFrame = nil
|
||||
zoomWindow = nil
|
||||
else
|
||||
print("> hiding")
|
||||
originalFrame = zoomWindow:frame()
|
||||
local screen = zoomWindow:screen()
|
||||
local frame = zoomWindow:frame()
|
||||
frame.x = screen:frame().w + 99000
|
||||
frame.y = screen:frame().h + 99000
|
||||
zoomWindow:setFrame(frame)
|
||||
end
|
||||
else
|
||||
print("> window not found")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
return obj
|
||||
+4
-1
@@ -57,7 +57,7 @@ function obj:init()
|
||||
self.launcher:bind("ctrl", "space", function() end)
|
||||
self.launcher:bind("", "return", function()
|
||||
-- self:switch("@wezterm@")
|
||||
self:switch("/Applications/Ghostty.app")
|
||||
self:switch("@ghostty@")
|
||||
end)
|
||||
self.launcher:bind("", "C", function()
|
||||
self:switch("Calendar.app")
|
||||
@@ -80,6 +80,9 @@ function obj:init()
|
||||
self.launcher:bind("", "H", function()
|
||||
self:switch("Hammerspoon.app")
|
||||
end)
|
||||
self.launcher:bind("", "L", function()
|
||||
self:switch("Claude.app")
|
||||
end)
|
||||
self.launcher:bind("", "M", function()
|
||||
self:switch("Messages.app")
|
||||
end)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.services.hammerspoon;
|
||||
# inherit (config.nmasur.settings) username;
|
||||
in
|
||||
|
||||
{
|
||||
@@ -15,25 +16,47 @@ in
|
||||
lib.mkEnableOption "Hammerspoon macOS automation";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.file.".Brewfile".text = /* homebrew */ ''
|
||||
cask "hammerspoon" # Different scroll style for mouse vs. trackpad
|
||||
'';
|
||||
|
||||
targets.darwin.defaults = {
|
||||
"org.hammerspoon.Hammerspoon" = {
|
||||
MJConfigFile = "${config.xdg.configHome}/hammerspoon/init.lua";
|
||||
};
|
||||
};
|
||||
|
||||
xdg.configFile."hammerspoon/init.lua".source = ./init.lua;
|
||||
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./Spoons/ControlEscape.spoon;
|
||||
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./Spoons/DismissAlerts.spoon;
|
||||
xdg.configFile."hammerspoon/Spoons/Launcher.spoon/init.lua".source =
|
||||
pkgs.replaceVars ./Spoons/Launcher.spoon/init.lua
|
||||
{
|
||||
discord = "${pkgs.discord}/Applications/Discord.app";
|
||||
discord = "Discord.app";
|
||||
firefox = "${pkgs.firefox-unwrapped}/Applications/Firefox.app";
|
||||
ghostty = "${pkgs.ghostty-bin}/Applications/Ghostty.app";
|
||||
obsidian = "${pkgs.obsidian}/Applications/Obsidian.app";
|
||||
slack = "${pkgs.slack}/Applications/Slack.app";
|
||||
wezterm = "${pkgs.wezterm}/Applications/WezTerm.app";
|
||||
zed = "${pkgs.zed-editor}/Applications/Zed.app";
|
||||
zed = "${config.programs.zed-editor.package}/Applications/Zed.app";
|
||||
};
|
||||
xdg.configFile."hammerspoon/Spoons/MoveWindow.spoon".source = ./Spoons/MoveWindow.spoon;
|
||||
xdg.configFile."hammerspoon/Spoons/HideZoomWindow.spoon".source = ./Spoons/HideZoomWindow.spoon;
|
||||
|
||||
home.activation.reloadHammerspoon = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.reload()"
|
||||
$DRY_RUN_CMD sleep 1
|
||||
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.console.clearConsole()"
|
||||
# The `hs` CLI blocks forever on Hammerspoon's IPC message port when the
|
||||
# app isn't running (|| true can't help — it never returns). Only reload
|
||||
# when the app is actually up, and wrap each call in `timeout` as a hard
|
||||
# backstop so activation can never hang even if IPC is unresponsive.
|
||||
hs=/Applications/Hammerspoon.app/Contents/Frameworks/hs/hs
|
||||
if /usr/bin/pgrep -x Hammerspoon > /dev/null 2>&1; then
|
||||
$DRY_RUN_CMD ${pkgs.coreutils}/bin/timeout 10 "$hs" -c "hs.reload()" || true
|
||||
$DRY_RUN_CMD sleep 1
|
||||
$DRY_RUN_CMD ${pkgs.coreutils}/bin/timeout 10 "$hs" -c "hs.console.clearConsole()" || true
|
||||
else
|
||||
verboseEcho "Hammerspoon not running; skipping reload."
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ hs.loadSpoon("ControlEscape"):start() -- Load Hammerspoon bits from https://gith
|
||||
hs.loadSpoon("Launcher"):init()
|
||||
hs.loadSpoon("DismissAlerts"):init()
|
||||
hs.loadSpoon("MoveWindow"):init()
|
||||
hs.loadSpoon("HideZoomWindow"):init()
|
||||
|
||||
@@ -214,9 +214,9 @@ in
|
||||
cfg.commands.lockScreen != null
|
||||
) "exec ${cfg.commands.lockScreen}";
|
||||
"${modifier}+Mod1+h" =
|
||||
''exec --no-startup-id ${lib.getExe cfg.terminal} --command="${pkgs.home-manager}/bin/home-manager switch --flake ${config.nmasur.presets.programs.dotfiles.path}#''${hostname} || read" '';
|
||||
''exec --no-startup-id ${lib.getExe cfg.terminal} -e "${lib.getExe config.nmasur.presets.programs.nixpkgs.commands.rebuildHome} '';
|
||||
"${modifier}+Mod1+r" =
|
||||
"exec --no-startup-id ${lib.getExe cfg.terminal} --command='doas nixos-rebuild switch --flake ${config.nmasur.presets.programs.dotfiles.path} || read'";
|
||||
"exec --no-startup-id ${lib.getExe cfg.terminal} -e ${lib.getExe config.nmasur.presets.programs.nixpkgs.commands.rebuildNixos}";
|
||||
|
||||
# Window options
|
||||
"${modifier}+q" = "kill";
|
||||
|
||||
@@ -43,14 +43,14 @@ in
|
||||
};
|
||||
|
||||
# Automatically check for mail and keep files synced locally
|
||||
services.mbsync = lib.mkIf pkgs.stdenv.isLinux {
|
||||
services.mbsync = lib.mkIf pkgs.stdenv.hostPlatform.isLinux {
|
||||
enable = true;
|
||||
frequency = "*:0/5";
|
||||
postExec = "${lib.getExe pkgs.notmuch} new";
|
||||
};
|
||||
|
||||
# Used to watch for new mail and trigger sync
|
||||
services.imapnotify.enable = pkgs.stdenv.isLinux;
|
||||
services.imapnotify.enable = pkgs.stdenv.hostPlatform.isLinux;
|
||||
|
||||
# Allows sending email from CLI/sendmail
|
||||
programs.msmtp.enable = true;
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.services.menubar;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.services.menubar.enable = lib.mkEnableOption "macOS menubar settings";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.packages = [
|
||||
pkgs.ice-bar # Menu bar hiding
|
||||
];
|
||||
|
||||
targets.darwin.defaults = {
|
||||
NSGlobalDomain = {
|
||||
|
||||
# Only hide menu bar in fullscreen
|
||||
_HIHideMenuBar = false;
|
||||
|
||||
NSStatusItemSelectionPadding = 6;
|
||||
NSStatusItemSpacing = 6;
|
||||
|
||||
};
|
||||
|
||||
"com.apple.menuextra.clock" = {
|
||||
# Show seconds on the clock
|
||||
ShowSeconds = true;
|
||||
|
||||
FlashDateSeparator = false;
|
||||
};
|
||||
|
||||
"leits.MeetingBar" = {
|
||||
eventTimeFormat = ''"show"'';
|
||||
eventTitleFormat = ''"none"'';
|
||||
eventTitleIconFormat = ''"iconCalendar"'';
|
||||
slackBrowser = ''{"deletable":true,"arguments":"","name":"Slack","path":""}'';
|
||||
zoomBrowser = ''{"deletable":true,"arguments":"","name":"Zoom","path":""}'';
|
||||
teamsBrowser = ''{"deletable":true,"arguments":"","name":"Teams","path":""}'';
|
||||
KeyboardShortcuts_joinEventShortcut = ''{"carbonModifiers":6400,"carbonKeyCode":38}'';
|
||||
timeFormat = ''"12-hour"'';
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -15,7 +15,7 @@ in
|
||||
|
||||
home.username = config.nmasur.settings.username;
|
||||
home.homeDirectory =
|
||||
if pkgs.stdenv.isDarwin then "/Users/${config.home.username}" else "/home/${config.home.username}";
|
||||
if pkgs.stdenv.hostPlatform.isDarwin then "/Users/${config.home.username}" else "/home/${config.home.username}";
|
||||
|
||||
home.packages = [
|
||||
pkgs.dig # DNS lookup
|
||||
@@ -33,6 +33,7 @@ in
|
||||
nmasur.presets = {
|
||||
programs = {
|
||||
bat.enable = lib.mkDefault true;
|
||||
chawan.enable = lib.mkDefault true;
|
||||
fd.enable = lib.mkDefault true;
|
||||
ripgrep.enable = lib.mkDefault true;
|
||||
starship.enable = lib.mkDefault true;
|
||||
|
||||
@@ -17,33 +17,24 @@ in
|
||||
|
||||
nmasur.presets = {
|
||||
fonts.enable = lib.mkDefault true;
|
||||
services.hammerspoon.enable = lib.mkDefault true;
|
||||
programs.nixpkgs-darwin.enable = lib.mkDefault true;
|
||||
programs.mpv.enable = lib.mkDefault true;
|
||||
services = {
|
||||
darwin-settings.enable = lib.mkDefault true;
|
||||
dock.enable = lib.mkDefault true;
|
||||
finder.enable = lib.mkDefault true;
|
||||
hammerspoon.enable = lib.mkDefault true;
|
||||
menubar.enable = lib.mkDefault true;
|
||||
};
|
||||
programs = {
|
||||
calibre.enable = lib.mkDefault true;
|
||||
fish-darwin.enable = lib.mkDefault true;
|
||||
homebrew.enable = lib.mkDefault true;
|
||||
nixpkgs-darwin.enable = lib.mkDefault true;
|
||||
mpv.enable = lib.mkDefault true;
|
||||
noti.enable = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
|
||||
home.homeDirectory = lib.mkForce "/Users/${config.home.username}";
|
||||
|
||||
# Default shell setting doesn't work
|
||||
home.sessionVariables = {
|
||||
SHELL = "${pkgs.fish}/bin/fish";
|
||||
};
|
||||
|
||||
# Used for aerc
|
||||
xdg.enable = lib.mkDefault pkgs.stdenv.isDarwin;
|
||||
|
||||
programs.fish.shellAbbrs.t = "trash";
|
||||
|
||||
# Add homebrew paths to CLI path
|
||||
home.sessionPath = [
|
||||
"/opt/homebrew/bin/"
|
||||
"/opt/homebrew/opt/trash/bin"
|
||||
];
|
||||
|
||||
home.packages = [
|
||||
pkgs.noti # Create notifications programmatically
|
||||
pkgs.ice-bar # Menu bar hiding
|
||||
];
|
||||
};
|
||||
|
||||
# Fix for: 'Error: HOME is set to "/var/root" but we expect "/var/empty"'
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.profiles.darwin-gaming;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.profiles.darwin-gaming.enable = lib.mkEnableOption "gaming config for macOS";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.file.".Brewfile".text = /* homebrew */ ''
|
||||
# Casks (GUI Apps)
|
||||
cask "steam" # Not packaged for Nix on macOS
|
||||
cask "epic-games" # Not packaged for Nix
|
||||
'';
|
||||
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,10 @@ in
|
||||
pkgs.pgcli # Postgres client with autocomplete
|
||||
];
|
||||
|
||||
nmasur.presets = {
|
||||
programs.cargo.enable = lib.mkDefault true;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -17,19 +17,31 @@ in
|
||||
|
||||
nmasur.presets.programs = {
|
||||
zed-editor.enable = lib.mkDefault true;
|
||||
jujutsu.enable = lib.mkDefault true;
|
||||
};
|
||||
|
||||
home.packages = [
|
||||
|
||||
# pkgs.nmasur.ren-find # Rename files
|
||||
# pkgs.nmasur.rep-grep # Replace text in files
|
||||
|
||||
# Charm tools
|
||||
|
||||
pkgs.glow # Markdown previews
|
||||
pkgs.skate # Key-value store
|
||||
pkgs.charm # Manage account and filesystem
|
||||
pkgs.pop # Send emails from a TUI
|
||||
# pkgs.crush # AI LLM Agent
|
||||
|
||||
pkgs.chawan # Browser TUI
|
||||
# Other utilities
|
||||
|
||||
pkgs.snitch # Network
|
||||
pkgs.comma # Nix-index auto-launch
|
||||
pkgs.nix-inspect # TUI for browsing Nix configs
|
||||
|
||||
# LLM
|
||||
|
||||
pkgs.antigravity-cli # AI LLM Agent
|
||||
pkgs.pi-coding-agent # AI LLM Agent
|
||||
|
||||
];
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@ in
|
||||
publicShare = lib.mkDefault "$HOME/other/public";
|
||||
templates = lib.mkDefault "$HOME/other/templates";
|
||||
extraConfig = {
|
||||
XDG_DEV_DIR = lib.mkDefault "$HOME/dev";
|
||||
DEV = lib.mkDefault "$HOME/dev";
|
||||
};
|
||||
setSessionVariables = true;
|
||||
};
|
||||
|
||||
nmasur.presets.programs = {
|
||||
|
||||
@@ -32,6 +32,7 @@ in
|
||||
nsxiv.enable = lib.mkDefault true;
|
||||
obsidian.enable = lib.mkDefault true;
|
||||
rofi.enable = lib.mkDefault true;
|
||||
thunderbird.enable = lib.mkDefault true;
|
||||
xclip.enable = lib.mkDefault true;
|
||||
zathura.enable = lib.mkDefault true;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.profiles.llm-development;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.profiles.llm-development.enable = lib.mkEnableOption "LLM coding tools";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
home.packages = [
|
||||
|
||||
pkgs.pi-coding-agent # AI LLM Agent
|
||||
|
||||
];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,24 +16,23 @@ in
|
||||
pkgs.age # Encryption
|
||||
pkgs.bc # Calculator
|
||||
pkgs.bottom # System monitor (top)
|
||||
pkgs.csvlens # CSV viewer (easier than visidata)
|
||||
pkgs.delta # Fancy diffs
|
||||
pkgs.difftastic # Other fancy diffs
|
||||
pkgs.doggo # DNS client (dig)
|
||||
pkgs.du-dust # Disk usage tree (ncdu)
|
||||
pkgs.dust # Disk usage tree (ncdu)
|
||||
pkgs.dua # File sizes (du)
|
||||
pkgs.duf # Basic disk information (df)
|
||||
pkgs.jless # JSON viewer
|
||||
pkgs.jo # JSON output
|
||||
pkgs.mpd # TUI slideshows
|
||||
pkgs.nixfmt-rfc-style # Format Nix code
|
||||
pkgs.stable.mpd # TUI slideshows
|
||||
pkgs.nixfmt # Format Nix code
|
||||
pkgs.nmasur.jqr # FZF fq JSON tool
|
||||
pkgs.nmasur.osc # Clipboard over SSH
|
||||
pkgs.nmasur.ren-find # Rename files
|
||||
pkgs.nmasur.rep-grep # Replace text in files
|
||||
pkgs.pandoc # Convert text documents
|
||||
pkgs.qrencode # Generate qr codes
|
||||
pkgs.spacer # Output lines in terminal
|
||||
pkgs.tealdeer # Cheatsheets
|
||||
pkgs.stable.tealdeer # Cheatsheets
|
||||
pkgs.tree # Print tree in terminal
|
||||
pkgs.vimv-rs # Batch rename files
|
||||
pkgs.yazi # TUI file explorer
|
||||
@@ -42,7 +41,7 @@ in
|
||||
|
||||
programs.fish.shellAliases = {
|
||||
"du" = lib.mkDefault (lib.getExe pkgs.dua);
|
||||
"ncdu" = lib.mkDefault (lib.getExe pkgs.du-dust);
|
||||
"ncdu" = lib.mkDefault (lib.getExe pkgs.dust);
|
||||
"df" = lib.mkDefault (lib.getExe pkgs.duf);
|
||||
|
||||
# Use eza (exa) instead of ls for fancier output
|
||||
@@ -63,8 +62,10 @@ in
|
||||
ghostty.enable = lib.mkDefault true;
|
||||
git.enable = lib.mkDefault true;
|
||||
helix.enable = lib.mkDefault true;
|
||||
jujutsu.enable = lib.mkDefault true;
|
||||
lag-triage.enable = lib.mkDefault true;
|
||||
lazygit.enable = lib.mkDefault true;
|
||||
neovim.enable = lib.mkDefault true;
|
||||
# neovim.enable = lib.mkDefault true;
|
||||
nix-index.enable = lib.mkDefault true;
|
||||
nixpkgs.enable = lib.mkDefault true;
|
||||
notes.enable = lib.mkDefault true;
|
||||
|
||||
@@ -21,7 +21,9 @@ in
|
||||
];
|
||||
|
||||
home.packages = [
|
||||
pkgs.visidata # CSV inspector
|
||||
pkgs.actionlint # Lint for GitHub Actions Workflows
|
||||
pkgs.claude-code # AI LLM Agent
|
||||
pkgs.stable.visidata # CSV inspector
|
||||
pkgs.dos2unix # Convert Windows text files
|
||||
pkgs.inetutils # Includes telnet
|
||||
pkgs.gnupg # Encryption
|
||||
@@ -29,6 +31,7 @@ in
|
||||
pkgs.ssm-session-manager-plugin
|
||||
pkgs.awslogs
|
||||
pkgs.stu # TUI for AWS S3
|
||||
pkgs.pre-commit
|
||||
pkgs.google-cloud-sdk
|
||||
pkgs.vault-bin
|
||||
pkgs.ipcalc # Make IP network calculations
|
||||
@@ -37,18 +40,28 @@ in
|
||||
pkgs.nmasur.ocr # Converts images to text
|
||||
pkgs.nmasur.aws-ec2 # Browse EC2 instances
|
||||
pkgs.nmasur.terraform-init # Quick shortcut for initializing Terraform backend
|
||||
pkgs.uv # Python packaging
|
||||
];
|
||||
|
||||
home.file.".Brewfile".text = /* homebrew */ ''
|
||||
# cask "1password" # 1Password will not launch from Nix on macOS
|
||||
# cask "gitify" # Git notifications in menu bar (downgrade manually from 4.6.1)
|
||||
# cask "logitech-g-hub" # Mouse and keyboard management
|
||||
cask "logitune" # Logitech webcam firmware
|
||||
cask "meetingbar" # Show meetings in menu bar
|
||||
'';
|
||||
|
||||
programs.fish.shellAliases.ec2 = "aws-ec2";
|
||||
|
||||
nmasur.presets = {
|
||||
fonts.enable = lib.mkDefault true;
|
||||
programs = {
|
||||
_1password.enable = lib.mkDefault true;
|
||||
# _1password.enable = lib.mkDefault true;
|
||||
atuin.enable = lib.mkDefault true;
|
||||
aws-ssh.enable = lib.mkDefault true;
|
||||
bash.enable = lib.mkDefault true;
|
||||
bat.enable = lib.mkDefault true;
|
||||
cargo.enable = lib.mkDefault true;
|
||||
direnv.enable = lib.mkDefault true;
|
||||
dotfiles.enable = lib.mkDefault true;
|
||||
fd.enable = lib.mkDefault true;
|
||||
@@ -67,8 +80,12 @@ in
|
||||
ripgrep.enable = lib.mkDefault true;
|
||||
starship.enable = lib.mkDefault true;
|
||||
terraform.enable = lib.mkDefault true;
|
||||
thunderbird.enable = lib.mkDefault true;
|
||||
weather.enable = lib.mkDefault true;
|
||||
};
|
||||
services = {
|
||||
mbsync.enable = lib.mkDefault true;
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.programs._1password;
|
||||
in
|
||||
|
||||
{
|
||||
options.nmasur.presets.programs._1password.enable = lib.mkEnableOption "1Password password manager";
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Unfree packages are already permitted via the shared nixpkgs instance
|
||||
# created in lib/default.nix (config.allowUnfree = true). Setting
|
||||
# nixpkgs.config here would conflict with nixpkgs.pkgs being set externally.
|
||||
programs._1password.enable = true;
|
||||
programs._1password-gui = {
|
||||
enable = true;
|
||||
# Certain features, including CLI integration and system authentication support,
|
||||
# require enabling PolKit integration on some desktop environments (e.g. Plasma).
|
||||
# polkitPolicyOwners = [ "yourUsernameHere" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -23,5 +23,19 @@ in
|
||||
|
||||
# Speeds up fish launch time on macOS
|
||||
programs.fish.useBabelfish = true;
|
||||
|
||||
programs.fish.shellInit = ''
|
||||
set -g __nixos_path_original $PATH
|
||||
function __nixos_path_fix -d "fix PATH value"
|
||||
set -l result (string split ":" $__nixos_path_original)
|
||||
for elt in $PATH
|
||||
if not contains -- $elt $result
|
||||
set -a result $elt
|
||||
end
|
||||
end
|
||||
set -g PATH $result
|
||||
end
|
||||
__nixos_path_fix
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ in
|
||||
};
|
||||
global = {
|
||||
brewfile = true; # Run brew bundle from anywhere
|
||||
lockfiles = false; # Don't save lockfile (since running from anywhere)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.nmasur.presets.security.corporateCa;
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
options.nmasur.presets.security.corporateCa = {
|
||||
|
||||
enable = lib.mkEnableOption "trusting a corporate MITM root CA for TLS interception";
|
||||
|
||||
certFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "/etc/ssl/corp-ca/CorpCA.pem";
|
||||
description = ''
|
||||
Absolute path to a PEM-encoded corporate root CA certificate to add to
|
||||
the system trust store.
|
||||
|
||||
Kept out of this (public) repo on purpose, so it points at a file you
|
||||
drop on the machine by hand. Passed as a string rather than a Nix path
|
||||
literal so pure flake evaluation does not try to read the out-of-repo
|
||||
file at eval time; it is read at build time instead (Darwin builds run
|
||||
without a sandbox).
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
|
||||
# Append the corporate root to /etc/ssl/certs/ca-certificates.crt, which
|
||||
# both the Nix daemon and (via NIX_SSL_CERT_FILE) client-side flake fetches
|
||||
# read. Without this, fetches behind the corporate TLS-intercepting proxy
|
||||
# fail with "self-signed certificate in certificate chain".
|
||||
security.pki.certificateFiles = [ cfg.certFile ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user