mirror of
https://github.com/nmasur/dotfiles
synced 2026-09-14 17:38:11 +00:00
Compare commits
188
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6209c039b | ||
|
|
3e246645a2 | ||
|
|
ed8be9dee6 | ||
|
|
ab98a202da | ||
|
|
e00560c42c | ||
|
|
d4e56dd190 | ||
|
|
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,220 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-09-07
|
||||||
|
|
||||||
|
- **Configured OpenID Connect (OIDC) authentication for Paperless-ngx**:
|
||||||
|
- Added secret management for `paperless-oidc-secret.age` via `secrets.paperless-oidc-secret` owned by `paperless:paperless` (0440).
|
||||||
|
- Configured `systemd.services.paperless-oidc-secret-secret` to run before and be required by all Paperless units, dynamically generating `/var/private/paperless-env` with `PAPERLESS_SOCIALACCOUNT_PROVIDERS` configured for Pocket ID (`auth.masu.rs`) using `client_id = "e6ff7fce-8e67-4c66-8f32-5ec3db4740a9"`, `oauth_pkce_enabled = true`, and email authentication linking (`email_authentication = true`, `verified_email = true`) in the native `APPS` schema.
|
||||||
|
- Added `services.paperless.environmentFile = "${config.secretsDirectory}/paperless-env"` to securely pass the OIDC configuration to Paperless services without exposing secrets in the Nix store.
|
||||||
|
- Configured Paperless settings to enable `allauth.socialaccount.providers.openid_connect` in `PAPERLESS_APPS` and enabled `PAPERLESS_REDIRECT_LOGIN_TO_SSO`.
|
||||||
|
- Configured reverse proxy trust settings (`PAPERLESS_USE_X_FORWARD_HOST`, `PAPERLESS_PROXY_SSL_HEADER`, and `PAPERLESS_TRUSTED_PROXIES`) to ensure callback URIs preserve the `https://` scheme behind Caddy.
|
||||||
|
- Fixed `systemd.services.paperless-secret` to order before and be required by `paperless-scheduler.service` instead of non-existent `paperless.service`.
|
||||||
|
|
||||||
|
- **Configured real client IP forwarding for Jellyfin reverse proxy**:
|
||||||
|
- Added a `map` handler to Jellyfin's Caddy route in `platforms/nixos/modules/nmasur/presets/services/jellyfin.nix` to resolve `{client_ip}` using Cloudflare's `CF-Connecting-IP` header when available, falling back to `{http.request.remote.host}` for direct local LAN connections.
|
||||||
|
- Configured `reverse_proxy.headers.request.set` to forward `X-Real-IP`, `X-Forwarded-For`, and `X-Forwarded-Proto` with the resolved client IP and request scheme to Jellyfin.
|
||||||
|
|
||||||
|
- **Configured OpenID Connect (OIDC) authentication for Nextcloud**:
|
||||||
|
- Added `user_oidc` to `services.nextcloud.extraApps`.
|
||||||
|
- Added secret management for `nextcloud-oidc-secret.age` via `secrets.nextcloud-oidc-secret` with owner `nextcloud` and group `nextcloud` (0440).
|
||||||
|
- Configured `systemd.services.nextcloud-oidc-secret-secret` to be required by and order before `nextcloud-setup.service`.
|
||||||
|
- Set `services.nextcloud.settings.overwriteprotocol = "https"` to ensure correct scheme handling for redirect URIs behind reverse proxy.
|
||||||
|
- Configured automated idempotent upsert of the Pocket ID provider via `systemd.services.nextcloud-setup.postStart` running `nextcloud-occ user_oidc:provider pocket-id` with client ID `c8a32c58-a781-4f14-9070-f498fdfda438`, `--clientsecret-file`, discovery URI `https://${hostnames.auth}/.well-known/openid-configuration`, `--mapping-uid="preferred_username"`, and `--unique-uid=0` to connect OIDC logins directly to existing local Nextcloud accounts.
|
||||||
|
- Updated `docs/oidc-services.md` with the verified configuration and callback URI (`https://cloud.masu.rs/apps/user_oidc/code`).
|
||||||
|
|
||||||
|
## 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-09-03: Ctrl-b did nothing because home-manager was never switched; two-phase toggle
|
||||||
|
|
||||||
|
- "Ctrl-b does nothing" root cause: the binding was built into the flake but never activated. The deployed `~/.config/fish/functions/` had no `heal-autosuggest.fish` and no `\cb` binding (0 matches), while the freshly-built config had both. On this setup a system rebuild does not switch home-manager — activation needs the home rebuild (`rebuild-home` / the Alt-Shift-H binding, i.e. `home-manager switch --flake`) followed by a fresh pane so `config.fish` re-runs `fish_user_key_bindings`. No fix works until it is actually activated; this should be the FIRST check next time a "did nothing" is reported.
|
||||||
|
- Made `heal-autosuggest` a two-phase toggle (chosen by the user over `exec fish`, to preserve the session): disable autosuggestions, `commandline -f repaint`, re-enable, `commandline -f repaint`. Grounded in `reader.rs` `update_autosuggestion`, which clears the wedged `in_flight_autosuggest_request` only on a repaint taken while autosuggestions are disabled. The old one-liner and the `fish_postexec` hook did a single back-to-back `set 0; set 1` with no disabled-state repaint, which likely never flushed the stuck request — matching the manual cure, which had prompt cycles between the off and the on. Still unverified against the real bug (not reproducible in a harness); confirm by activating, then pressing Ctrl-b in a live lagging shell.
|
||||||
|
- If the two-phase toggle still does not cure once activated: fall back to `exec fish` on the key (guaranteed per the user's day-one report that a fresh shell always fixes it), and capture a flight-recorder log for the upstream fish report.
|
||||||
|
|
||||||
|
## 2026-09-01: postexec hook fires but does not cure — moved heal to a keybinding
|
||||||
|
|
||||||
|
- Honest status: the `fish_postexec` self-heal hook IS registered and DOES fire (verified in the real config), yet the lag persists. So toggling `fish_autosuggestion_enabled` off/on from `fish_postexec` does not cure it, even though the user typing the same `set … 0; set … 1` at the prompt does.
|
||||||
|
- Why (code-level): variable dispatch is synchronous (`env_dispatch.rs` → `reader_set_autosuggestion_enabled` on every `set`), so the two sets net to no change and schedule a repaint. That repaint only has effect from inside the reader's active input loop. `fish_postexec` runs BETWEEN commands, outside that loop, so its effect is superseded before the next prompt. A key binding runs inside the loop; `fish_postexec` cannot.
|
||||||
|
- Change: bound **Ctrl-b** to a new `heal-autosuggest` function (toggle + `commandline -f repaint`) via `fish_user_key_bindings` (Ctrl-g was already taken). Verified that when `fish_user_key_bindings` runs to completion — as it does in the real config, since the existing `\cn` etc. work (`__fish_config_interactive.fish:105`) — `\cb` binds to heal-autosuggest and the toggle resets the variable. NOT verified to cure the real lag: the bug still cannot be reproduced in a harness, so only a test in a live lagging shell can confirm. The `fish_postexec`/`fish_cancel` hook is kept (harmless) but is no longer considered the fix.
|
||||||
|
- Guaranteed fallback if Ctrl-b does not cure: a fresh shell (`exec fish`), which the user has confirmed from the very start always fixes it — Ctrl-b can be rebound to that. The only path to a truly automatic fix is a flight-recorder capture (`~/.local/state/lag-triage/RECORD`) of the reader during an actual episode, then an upstream fish report.
|
||||||
|
|
||||||
|
## 2026-08-31 (evening): self-heal hook was never firing — fish cannot autoload event handlers
|
||||||
|
|
||||||
|
- The `__autosuggestion_unwedge` hook did not work because it was installed via `programs.fish.functions`, which writes to fish's **autoload** directory — and fish only registers `--on-event` handlers when a function is actually loaded, which never happens for a hook nothing calls by name. Verified in a PTY test: the autoloaded handler never fires; the identical definition `source`d eagerly fires immediately. (The zellij module's `__fish_update_cwd_osc` works as an autoloaded event function only because it overrides a function fish itself loads.)
|
||||||
|
- Meanwhile the user confirmed the instant back-to-back toggle (`set -g fish_autosuggestion_enabled 0 && set -g fish_autosuggestion_enabled 1`) cures a lagging shell — so the handler body is right; only its registration was broken.
|
||||||
|
- Fix: the handler is now defined eagerly in `config.fish` via `programs.fish.interactiveShellInit` (lag-triage module), registered on **fish_postexec** (fires after every command — the moment TUIs exit) and **fish_cancel** (fires on Ctrl-C at the prompt), so a bare Ctrl-C is an instant no-command cure. Verified in an interactive PTY against the actual nix-generated snippet: registers at startup, fires on both events, still respects a deliberate manual disable.
|
||||||
|
- Coverage note: if a wedge forms with no command running (and no Ctrl-C), it heals at the next command; worst-case lag window is "until you run anything or press Ctrl-C".
|
||||||
|
|
||||||
|
## 2026-08-31 (later): automatic self-heal hook
|
||||||
|
|
||||||
|
- Confirmed by A/B in the live shell: after curing the lag with `set -g fish_autosuggestion_enabled 0`, re-enabling with `1` does **not** bring the lag back — the toggle resets the wedged autosuggestion state rather than merely masking it.
|
||||||
|
- Added `__autosuggestion_unwedge` (lag-triage module): a `fish_postexec` event handler that toggles `fish_autosuggestion_enabled` off/on after every command — i.e. at the exact moment a TUI has just exited, when the wedge forms. Builtins only, no visible output (verified in an interactive PTY test), and it skips the reset when the user has deliberately disabled autosuggestions.
|
||||||
|
- Honest caveat: the manual cure had keystrokes between the off and the on; whether the instant off/on inside an event handler resets the same reader-internal state is unproven. The flight recorder therefore STAYS ARMED (`~/.local/state/lag-triage/RECORD`) until the hook has survived normal use for a while. If lag recurs despite the hook: cure manually (`set … 0`, type a few chars, `set … 1`), and keep the flight log for that pid — then the hook needs the stronger form (disable at postexec, re-enable one prompt-cycle later, scoped to TUI commands).
|
||||||
|
- Limitations by design: the hook fires only in shells that run commands, so a wedge formed without any command executing in that shell (if that is possible — e.g. floating-pane TUIs never touch the pane shell) would not be healed until the next command runs there.
|
||||||
|
|
||||||
|
## 2026-08-31: culprit confirmed — fish's autosuggestion pipeline
|
||||||
|
|
||||||
|
- A/B test in a live lagging shell (pid 56089): `set -g fish_autosuggestion_enabled 0` (builtin only, nothing else) **instantly cured the lag**. The post-TUI typing lag is in fish 4.8.1's autosuggestion pipeline.
|
||||||
|
- Sampling that shell afterwards showed it had **only one thread** (the main thread): the poisoned state is main-thread-side bookkeeping, not a hung worker still sitting in the process. Source review (`src/threads/threads.rs`, `src/threads/debounce.rs`): `ThreadPool::perform` silently queues work with no spawn and no wake when it believes `total_threads == max_threads` — a leaked `total_threads` count (workers that died without decrementing, e.g. across a TUI's lifetime) would strand all future autosuggestion work forever; the Debounce then abandons its token every 500ms and re-enqueues per keystroke. The exact step that delays keystroke *echo* is still unproven — the flight recorder (armed via `~/.local/state/lag-triage/RECORD`) logs the reader's per-keystroke behavior and will capture it on the next occurrence in a recorded shell.
|
||||||
|
- Precedent: fish had a closely-related bug class before (#11841 — unread terminal query responses "causing noticeable lags"). No fish release newer than 4.8.1 exists, so no upstream fix to adopt; an upstream report with the flight-recorder capture is the path to a real fix.
|
||||||
|
- Practical interim cure (harmless, instant, in the lagging shell): `set -g fish_autosuggestion_enabled 0`, and re-enable with `1` — whether lag returns on re-enable is the next discriminating datum.
|
||||||
|
|
||||||
|
## 2026-08-30 (later): sampler attach CURES the lag — wedged-thread evidence + flight recorder
|
||||||
|
|
||||||
|
- Major new datum: in a lagging shell, running `mkdir` + `/usr/bin/sample $fish_pid … &` + `disown` **cured the lag instantly**, before any planned reset/toggle test could run. Plain external commands do NOT cure it (the 2026-08-29 triage ran many and the lag survived), so the distinguishing action is the sampler **attaching and suspending/resuming fish's threads**. Conclusion: a fish-internal thread/wait is wedged (missed wakeup or stuck blocking wait), and per-keystroke work at the main commandline stalls against it; suspension/resume kicks it loose. Consistent with: `read` prompts unaffected (no autosuggestion/highlight pipeline), subshells immune (fresh threads), raw input clean. The captured sample (`~/.local/state/lag-triage/fish-sample.txt`) shows only the post-cure state — sampling is a cure, not a capture.
|
||||||
|
- Therefore the observer must be running BEFORE the lag starts: the `fish-no-query-term` wrapper is now a **flight recorder** — `touch ~/.local/state/lag-triage/RECORD`, then every newly spawned pane shell logs `FISH_DEBUG=reader,term-support,proc-termowner,iothread,fd-monitor,topic-monitor` to `~/.local/state/lag-triage/flight/fish-<ts>-<pid>.log` (3-day auto-cleanup; remove RECORD to disable, zero overhead when off). When lag next occurs, the log already contains what each keystroke did during the lag.
|
||||||
|
- `lag-sample` now takes a PID and should be run from a DIFFERENT pane (`echo $fish_pid` — a builtin — in the lagging shell to get it), since attaching from inside cures the lag.
|
||||||
|
- **Next-occurrence checklist (in order, least perturbing first):** (1) in the lagging shell, builtins only: `set -g fish_autosuggestion_enabled 0` → type at the real commandline; if cured, the autosuggestion/debounce path is implicated (a worker thread was seen in `HistorySearch::go_to_next_match`); (2) still laggy: `fish_default_key_bindings` → test (vi-mode path); (3) from another pane: `kill -WINCH <pid>` → test, then `kill -CONT <pid>` → test (discriminates reader-wakeup vs generic unwedge; if WINCH cures, a window resize would too); (4) from another pane: `lag-sample <pid>` while typing in the lagging pane; (5) immediately save the flight log for that pid.
|
||||||
|
|
||||||
|
## 2026-08-30
|
||||||
|
|
||||||
|
- **The post-TUI typing lag is NOT resolved** by the `fish-no-query-term` wrapper: lag recurred in a fresh zellij session after exiting Claude Code, in a shell verified (via `ps eww`) to have `fish_features=no-query-term` in its environment. The query-term reader-degradation bug proven on 2026-08-29 is real (and the wrapper stays as hardening against it), but it is not the mechanism behind this lag. Downgraded the entry below from "root cause" to "a root cause".
|
||||||
|
- Known constraints on the real mechanism: per-keystroke lag at the main fish commandline; fish `read` prompts unaffected; raw input reaches the pane practical as plain bytes; a subshell/`exec fish` cures it (process-local state). Note the 2026-08-29 triage's reset ladder short-circuited on a false "y" at stage A, so stages B–G (mouse/keypad/altscreen/stty/DECSTR resets) were never actually tested against real lag.
|
||||||
|
- Added `lag-sample` (fish function): stack-samples the lagging fish process plus the zellij server/client via `/usr/bin/sample` for 8s while the user types at the commandline. This directly names where the time goes (fish reader? highlighting/autosuggestion threads? zellij render loop?) instead of inferring it. Next occurrence: run `lag-sample` in the lagging shell, type junk at the prompt until done, then inspect `~/.local/state/lag-triage/sample-*.txt`. Follow with `unlag` (full reset ladder, never yet truly tested), then A/B toggles: `set -g fish_autosuggestion_enabled 0`, `fish_default_key_bindings`.
|
||||||
|
|
||||||
|
## 2026-08-29 (a root cause found and fixed — but not THE lag)
|
||||||
|
|
||||||
|
- **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,275 @@
|
|||||||
|
# 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`:**
|
||||||
|
```nix
|
||||||
|
secrets.nextcloud-oidc-secret = {
|
||||||
|
source = ./nextcloud-oidc-secret.age;
|
||||||
|
dest = "${config.secretsDirectory}/nextcloud-oidc-secret";
|
||||||
|
owner = "nextcloud";
|
||||||
|
group = "nextcloud";
|
||||||
|
permissions = "0440";
|
||||||
|
};
|
||||||
|
systemd.services.nextcloud-oidc-secret-secret = {
|
||||||
|
requiredBy = [ "nextcloud-setup.service" ];
|
||||||
|
before = [ "nextcloud-setup.service" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nextcloud = {
|
||||||
|
settings.overwriteprotocol = "https";
|
||||||
|
extraApps = {
|
||||||
|
user_oidc = config.services.nextcloud.package.packages.apps.user_oidc;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.nextcloud-setup = {
|
||||||
|
after = [ "nextcloud-oidc-secret-secret.service" ];
|
||||||
|
postStart = ''
|
||||||
|
${config.services.nextcloud.occ}/bin/nextcloud-occ user_oidc:provider pocket-id \
|
||||||
|
--clientid="c8a32c58-a781-4f14-9070-f498fdfda438" \
|
||||||
|
--clientsecret-file="${config.secrets.nextcloud-oidc-secret.dest}" \
|
||||||
|
--discoveryuri="https://${hostnames.auth}/.well-known/openid-configuration" \
|
||||||
|
--scope="openid profile email" \
|
||||||
|
--mapping-uid="preferred_username" \
|
||||||
|
--unique-uid=0
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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_REDIRECT_LOGIN_TO_SSO = true;
|
||||||
|
};
|
||||||
|
# Configured via environmentFile to pass the client secret, enable PKCE, and link by email:
|
||||||
|
# PAPERLESS_SOCIALACCOUNT_PROVIDERS = builtins.toJSON {
|
||||||
|
# openid_connect = {
|
||||||
|
# APPS = [
|
||||||
|
# {
|
||||||
|
# provider_id = "pocket-id";
|
||||||
|
# name = "Pocket ID";
|
||||||
|
# client_id = "e6ff7fce-8e67-4c66-8f32-5ec3db4740a9";
|
||||||
|
# secret = "...";
|
||||||
|
# settings = {
|
||||||
|
# server_url = "https://auth.masu.rs";
|
||||||
|
# token_auth_method = "client_secret_basic";
|
||||||
|
# oauth_pkce_enabled = true;
|
||||||
|
# email_authentication = true;
|
||||||
|
# verified_email = true;
|
||||||
|
# };
|
||||||
|
# }
|
||||||
|
# ];
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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": {
|
"nodes": {
|
||||||
"cl-nix-lite": {
|
"cl-nix-lite": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-parts": "flake-parts",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems",
|
||||||
|
"treefmt-nix": "treefmt-nix"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1728174978,
|
"lastModified": 1782994178,
|
||||||
"narHash": "sha256-Grqqg+xuicANB85j0gNEXxi9SBKY7bzGeTuyi95eGcY=",
|
"narHash": "sha256-VGc3/2fe6hnOmNSLlOygEAbeS69v7A7rjKDKRzAgE2Q=",
|
||||||
"owner": "hraban",
|
"owner": "hraban",
|
||||||
"repo": "cl-nix-lite",
|
"repo": "cl-nix-lite",
|
||||||
"rev": "31cfe6275c341eb3120a99f4b1c8516c49a29d87",
|
"rev": "eb584721e5e799bafe0c6210a8a1a398f90e8ac0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -22,11 +28,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755825449,
|
"lastModified": 1786845137,
|
||||||
"narHash": "sha256-XkiN4NM9Xdy59h69Pc+Vg4PxkSm9EWl6u7k6D5FZ5cM=",
|
"narHash": "sha256-oQFip+v0luP8NIxJzmiW4Wu8bILsbFWom5l0zonl8hQ=",
|
||||||
"owner": "lnl7",
|
"owner": "lnl7",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "8df64f819698c1fee0c2969696f54a843b2231e8",
|
"rev": "4cff07de74b50e64bdd68cd4e722ab5b6b35ee48",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -43,11 +49,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756115622,
|
"lastModified": 1781152676,
|
||||||
"narHash": "sha256-iv8xVtmLMNLWFcDM/HcAPLRGONyTRpzL9NS09RnryRM=",
|
"narHash": "sha256-RxWs5ND31KzTG7wvMM+PMfUjyNpmIEr999lqNARaM5o=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "bafad29f89e83b2d861b493aa23034ea16595560",
|
"rev": "ff8702b4de27f72b4c78573dfb89ec74e36abdf1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -59,11 +65,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1730663653,
|
"lastModified": 1766808011,
|
||||||
"narHash": "sha256-kFCUWettiFHDIqxCWWQ9qY8pVh+Lj+XL0Giyy/kdomg=",
|
"narHash": "sha256-s83BS0abtIj7vzUf8JE0209KphfHA6qRw/92D9k/F+0=",
|
||||||
"owner": "hraban",
|
"owner": "hraban",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "e5b16676185cb7548581c852f51ce7f3a49bba5e",
|
"rev": "6e0aafdc4c4c2043c66f756d5045374b42184fc5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -76,11 +82,27 @@
|
|||||||
"flake-compat_2": {
|
"flake-compat_2": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747046372,
|
"lastModified": 1767039857,
|
||||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
"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",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -90,6 +112,24 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flake-parts": {
|
"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": {
|
"inputs": {
|
||||||
"nixpkgs-lib": [
|
"nixpkgs-lib": [
|
||||||
"nur",
|
"nur",
|
||||||
@@ -132,7 +172,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_2"
|
"systems": "systems_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1731533236,
|
||||||
@@ -148,27 +188,6 @@
|
|||||||
"type": "github"
|
"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": {
|
"home-manager": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -176,11 +195,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756496801,
|
"lastModified": 1789183968,
|
||||||
"narHash": "sha256-IYIsnPy+cJxe8RbDHBrCtfJY0ry2bG2H7WvMcewiGS8=",
|
"narHash": "sha256-pEWnYdIF1pBMZwarp/rEPzl+Lknhm5hhjSfeLxH8nAA=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "77a71380c38fb2a440b4b5881bbc839f6230e1cb",
|
"rev": "cd1c9e552f41894aeb5cc5cb353d5a1d61550357",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -195,18 +214,16 @@
|
|||||||
"cl-nix-lite": "cl-nix-lite",
|
"cl-nix-lite": "cl-nix-lite",
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs_3",
|
||||||
"nixpkgs"
|
"systems": "systems_2",
|
||||||
],
|
"treefmt-nix": "treefmt-nix_2"
|
||||||
"systems": "systems",
|
|
||||||
"treefmt-nix": "treefmt-nix"
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756057867,
|
"lastModified": 1783182903,
|
||||||
"narHash": "sha256-ziR5eQGqRWhW8tf8r0TIplaqNt+HXu1G1X41LUr4IYo=",
|
"narHash": "sha256-7IteXipJZfFepi5zakxe0jQ5i3vRBQguk0+Gtte1Fu4=",
|
||||||
"owner": "hraban",
|
"owner": "hraban",
|
||||||
"repo": "mac-app-util",
|
"repo": "mac-app-util",
|
||||||
"rev": "8414fa1e2cb775b17793104a9095aabeeada63ef",
|
"rev": "039f33deef21782d4db97087f426504951239887",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -215,6 +232,46 @@
|
|||||||
"type": "github"
|
"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": 1788680125,
|
||||||
|
"narHash": "sha256-amGSoDobwmp4CFvCn841ws2iuitus+HUJdD/gKgsrJA=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-index-database",
|
||||||
|
"rev": "116ad1c2adb642405ef8916f6a94c8626f971344",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nix-index-database",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nix2vim": {
|
"nix2vim": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
@@ -223,11 +280,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755344765,
|
"lastModified": 1779563638,
|
||||||
"narHash": "sha256-k/Cvh/mzb5lSvilKdgwNBCyAyYmD8YPr1nc0sTSgwxI=",
|
"narHash": "sha256-9CQKxDKDqVYdDhJW+A955AZwpwZjKUgvu0LbfsL6uyI=",
|
||||||
"owner": "gytis-ivaskevicius",
|
"owner": "gytis-ivaskevicius",
|
||||||
"repo": "nix2vim",
|
"repo": "nix2vim",
|
||||||
"rev": "78467c8de07719f92397179844bf75cdf2e58b83",
|
"rev": "6927414f0de10a18fb6bc9977717088777d50d0d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -259,11 +316,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751903740,
|
"lastModified": 1769813415,
|
||||||
"narHash": "sha256-PeSkNMvkpEvts+9DjFiop1iT2JuBpyknmBUs0Un0a4I=",
|
"narHash": "sha256-nnVmNNKBi1YiBNPhKclNYDORoHkuKipoz7EtVnXO50A=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixos-generators",
|
"repo": "nixos-generators",
|
||||||
"rev": "032decf9db65efed428afd2fa39d80f7089085eb",
|
"rev": "8946737ff703382fda7623b9fab071d037e897d5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -274,11 +331,58 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1754340878,
|
"lastModified": 1779796641,
|
||||||
"narHash": "sha256-lgmUyVQL9tSnvvIvBp7x1euhkkCho7n3TMzgjdvgPoU=",
|
"narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"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": 1789114715,
|
||||||
|
"narHash": "sha256-ugpsyk3NM2s87vXfUiIIiibbJ4Pp0JPS5p/3mfs+q+c=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "21a67dc470149f337cecafbe965d8d252a390518",
|
||||||
|
"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"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -288,29 +392,61 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1735563628,
|
"lastModified": 1782999065,
|
||||||
"narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=",
|
"narHash": "sha256-5Dgj5+pIQYZKrXUGaLCk7CKfN3MmpwIhO94++WVxvng=",
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798",
|
"rev": "80d591ed473cfc46329932c2aadac9b435342c7c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-24.05",
|
"ref": "nixos-26.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756386758,
|
"lastModified": 1770107345,
|
||||||
"narHash": "sha256-1wxxznpW2CKvI9VdniaUnTT2Os6rdRJcRUf65ZK9OtE=",
|
"narHash": "sha256-tbS0Ebx2PiA1FRW8mt8oejR0qMXmziJmPaU1d4kYY9g=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"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": 1789006805,
|
||||||
|
"narHash": "sha256-xB8mKMOx1IA9vTDNLmJZ6n4wCMq/cuWBBOzGCRnqxrU=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8ce4ef6cb6f871616146b9fe26d2a5ae594e94fe",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -320,13 +456,13 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_7": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1728538411,
|
"lastModified": 1744536153,
|
||||||
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -338,17 +474,17 @@
|
|||||||
},
|
},
|
||||||
"nur": {
|
"nur": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts_2",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1756524478,
|
"lastModified": 1789200175,
|
||||||
"narHash": "sha256-2oSBlcYCgwrVxUZwM8MV6hBFsfsWFbeN5ErQiCA+38s=",
|
"narHash": "sha256-JriLm/izGMXB4uAHWXKj13kXa3nKZj2r/sR5xHdisgs=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nur",
|
"repo": "nur",
|
||||||
"rev": "e82a8b0095f54edb6bbbb1d862f3da502dca1396",
|
"rev": "2c789298fc525713063e3b88359d02101651144f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -361,14 +497,16 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"darwin": "darwin",
|
"darwin": "darwin",
|
||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
"helix": "helix",
|
|
||||||
"home-manager": "home-manager",
|
"home-manager": "home-manager",
|
||||||
"mac-app-util": "mac-app-util",
|
"mac-app-util": "mac-app-util",
|
||||||
|
"nix-caddy-withplugins": "nix-caddy-withplugins",
|
||||||
|
"nix-index-database": "nix-index-database",
|
||||||
"nix2vim": "nix2vim",
|
"nix2vim": "nix2vim",
|
||||||
"nixos-generators": "nixos-generators",
|
"nixos-generators": "nixos-generators",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_6",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
"nur": "nur",
|
"nur": "nur",
|
||||||
|
"rust-overlay": "rust-overlay",
|
||||||
"wsl": "wsl",
|
"wsl": "wsl",
|
||||||
"zellij-switch": "zellij-switch",
|
"zellij-switch": "zellij-switch",
|
||||||
"zenyd-mpv-scripts": "zenyd-mpv-scripts"
|
"zenyd-mpv-scripts": "zenyd-mpv-scripts"
|
||||||
@@ -376,35 +514,14 @@
|
|||||||
},
|
},
|
||||||
"rust-overlay": {
|
"rust-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs_7"
|
||||||
"helix",
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1740623427,
|
"lastModified": 1789196581,
|
||||||
"narHash": "sha256-3SdPQrZoa4odlScFDUHd4CUPQ/R1gtH4Mq9u8CBiK8M=",
|
"narHash": "sha256-yJr1Bt4fKkKIpPYbsKGqJ0VFdoDnURgRwuffmBQ2WzY=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "d342e8b5fd88421ff982f383c853f0fc78a847ab",
|
"rev": "228ecefb6329d5a531b77b46b581a2f0c26ee056",
|
||||||
"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",
|
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -414,21 +531,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"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": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
@@ -443,6 +545,21 @@
|
|||||||
"type": "github"
|
"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": {
|
"systems_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1681028828,
|
"lastModified": 1681028828,
|
||||||
@@ -458,16 +575,49 @@
|
|||||||
"type": "github"
|
"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": {
|
"treefmt-nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755934250,
|
"lastModified": 1766000401,
|
||||||
"narHash": "sha256-CsDojnMgYsfshQw3t4zjRUkmMmUdZGthl16bXVWgRYU=",
|
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"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"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -478,17 +628,17 @@
|
|||||||
},
|
},
|
||||||
"wsl": {
|
"wsl": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat_2",
|
"flake-compat": "flake-compat_3",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1755261305,
|
"lastModified": 1789164534,
|
||||||
"narHash": "sha256-EOqCupB5X5WoGVHVcfOZcqy0SbKWNuY3kq+lj1wHdu8=",
|
"narHash": "sha256-DoYGPM6QpnYBLWj9gGw6ZwAzIX+HrAVov1BoT+8Jixo=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "NixOS-WSL",
|
"repo": "NixOS-WSL",
|
||||||
"rev": "203a7b463f307c60026136dd1191d9001c43457f",
|
"rev": "72c92b11bb8289e6651c7fef29cc0a885fd6a255",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -502,15 +652,17 @@
|
|||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"rust-overlay": "rust-overlay_2",
|
"rust-overlay": [
|
||||||
"systems": "systems_3"
|
"rust-overlay"
|
||||||
|
],
|
||||||
|
"systems": "systems_4"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1742588229,
|
"lastModified": 1781873766,
|
||||||
"narHash": "sha256-IPg0pBw0ciF+xl6viq3nK+dvZoDZrfBDui7dkPLz258=",
|
"narHash": "sha256-V8baEgRBa2SqtCLbQ4EaphIMKsnAogY2oMdttWYhkEs=",
|
||||||
"owner": "mostafaqanbaryan",
|
"owner": "mostafaqanbaryan",
|
||||||
"repo": "zellij-switch",
|
"repo": "zellij-switch",
|
||||||
"rev": "0e3c303c19890ccb03589230ac5a7c4307e573e4",
|
"rev": "ced3c681afe40785b2853e3a976908466786ee79",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -522,11 +674,11 @@
|
|||||||
"zenyd-mpv-scripts": {
|
"zenyd-mpv-scripts": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1707704915,
|
"lastModified": 1765050776,
|
||||||
"narHash": "sha256-9P/8q/OZXfaJMS08acQP4h3/zUA5mKRQee0JmkXcz1w=",
|
"narHash": "sha256-9gO+GkNoGsxAbMRrBWu0FfXEQtyTmHivlaxlYLpV2YM=",
|
||||||
"owner": "zenyd",
|
"owner": "zenyd",
|
||||||
"repo": "mpv-scripts",
|
"repo": "mpv-scripts",
|
||||||
"rev": "9bdce0050144cb24f92475f7bdd77180e0e4c26b",
|
"rev": "62f4bb313c6cb6366672e78dea940e9da8fec84a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
# Used for specific stable packages
|
# 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
|
# Used for MacOS system config
|
||||||
darwin = {
|
darwin = {
|
||||||
@@ -37,7 +37,13 @@
|
|||||||
# Better App install management in macOS
|
# Better App install management in macOS
|
||||||
mac-app-util = {
|
mac-app-util = {
|
||||||
url = "github:hraban/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
|
# Manage disk format and partitioning
|
||||||
@@ -52,6 +58,11 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-index-database = {
|
||||||
|
url = "github:nix-community/nix-index-database";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
# Convert Nix to Neovim config
|
# Convert Nix to Neovim config
|
||||||
nix2vim = {
|
nix2vim = {
|
||||||
url = "github:gytis-ivaskevicius/nix2vim";
|
url = "github:gytis-ivaskevicius/nix2vim";
|
||||||
@@ -64,17 +75,23 @@
|
|||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
rust-overlay = {
|
||||||
|
url = "github:oxalica/rust-overlay";
|
||||||
|
};
|
||||||
|
|
||||||
# Zellij Switcher
|
# Zellij Switcher
|
||||||
zellij-switch = {
|
zellij-switch = {
|
||||||
url = "github:mostafaqanbaryan/zellij-switch";
|
url = "github:mostafaqanbaryan/zellij-switch";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.rust-overlay.follows = "rust-overlay";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Text editor
|
# # Text editor
|
||||||
helix = {
|
# helix = {
|
||||||
url = "github:helix-editor/helix";
|
# url = "github:helix-editor/helix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
# inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
# inputs.rust-overlay.follows = "rust-overlay";
|
||||||
|
# };
|
||||||
|
|
||||||
# # Nextcloud Apps
|
# # Nextcloud Apps
|
||||||
# nextcloud-news = {
|
# nextcloud-news = {
|
||||||
@@ -111,6 +128,7 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
audiobooks = "read.${baseName}";
|
audiobooks = "read.${baseName}";
|
||||||
|
auth = "auth.${baseName}";
|
||||||
bookmarks = "keep.${baseName}";
|
bookmarks = "keep.${baseName}";
|
||||||
books = "books.${baseName}";
|
books = "books.${baseName}";
|
||||||
budget = "money.${baseName}";
|
budget = "money.${baseName}";
|
||||||
@@ -119,6 +137,7 @@
|
|||||||
download = "download.${baseName}";
|
download = "download.${baseName}";
|
||||||
files = "files.${baseName}";
|
files = "files.${baseName}";
|
||||||
git = "git.${baseName}";
|
git = "git.${baseName}";
|
||||||
|
hister = "hister.${baseName}";
|
||||||
imap = "imap.purelymail.com";
|
imap = "imap.purelymail.com";
|
||||||
influxdb = "influxdb.${baseName}";
|
influxdb = "influxdb.${baseName}";
|
||||||
irc = "irc.${baseName}";
|
irc = "irc.${baseName}";
|
||||||
@@ -126,6 +145,7 @@
|
|||||||
mathesar = "mathesar.${baseName}";
|
mathesar = "mathesar.${baseName}";
|
||||||
metrics = "metrics.${baseName}";
|
metrics = "metrics.${baseName}";
|
||||||
minecraft = "minecraft.${baseName}";
|
minecraft = "minecraft.${baseName}";
|
||||||
|
mumble = "mumble.${baseName}";
|
||||||
n8n = "n8n.${baseName}";
|
n8n = "n8n.${baseName}";
|
||||||
navidrome = "music.${baseName}";
|
navidrome = "music.${baseName}";
|
||||||
notifications = "ntfy.${baseName}";
|
notifications = "ntfy.${baseName}";
|
||||||
@@ -133,6 +153,7 @@
|
|||||||
photos = "photos.${baseName}";
|
photos = "photos.${baseName}";
|
||||||
postgresql = "pg.${baseName}";
|
postgresql = "pg.${baseName}";
|
||||||
prometheus = "prom.${baseName}";
|
prometheus = "prom.${baseName}";
|
||||||
|
recipes = "cooking.${baseName}";
|
||||||
secrets = "vault.${baseName}";
|
secrets = "vault.${baseName}";
|
||||||
smtp = "smtp.purelymail.com";
|
smtp = "smtp.purelymail.com";
|
||||||
status = "status.${baseName}";
|
status = "status.${baseName}";
|
||||||
@@ -140,38 +161,9 @@
|
|||||||
transmission = "transmission.${baseName}";
|
transmission = "transmission.${baseName}";
|
||||||
};
|
};
|
||||||
|
|
||||||
in
|
|
||||||
rec {
|
|
||||||
|
|
||||||
lib = import ./lib inputs;
|
lib = import ./lib inputs;
|
||||||
flattenAttrset = attrs: builtins.foldl' lib.mergeAttrs { } (builtins.attrValues attrs);
|
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 (
|
homeModules = builtins.mapAttrs (
|
||||||
system: hosts:
|
system: hosts:
|
||||||
builtins.mapAttrs (
|
builtins.mapAttrs (
|
||||||
@@ -179,19 +171,6 @@
|
|||||||
) hosts
|
) hosts
|
||||||
) lib.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
|
# Disk formatting, only used once
|
||||||
diskoConfigurations = {
|
diskoConfigurations = {
|
||||||
root = import ./hosts/x86_64-linux/swan/root.nix;
|
root = import ./hosts/x86_64-linux/swan/root.nix;
|
||||||
@@ -221,6 +200,51 @@
|
|||||||
) lib.linuxHosts # x86_64-linux = { arrow = ...; swan = ...; }
|
) 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 =
|
# packages =
|
||||||
# lib.forSystems lib.linuxSystems (
|
# lib.forSystems lib.linuxSystems (
|
||||||
# system: generateImagesForHosts system // lib.pkgsBySystem.${system}.nmasur
|
# system: generateImagesForHosts system // lib.pkgsBySystem.${system}.nmasur
|
||||||
@@ -280,7 +304,7 @@
|
|||||||
inherit (lib) overlays;
|
inherit (lib) overlays;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
pkgs.nixfmt-rfc-style
|
pkgs.nixfmt
|
||||||
);
|
);
|
||||||
|
|
||||||
# Templates for starting other projects quickly
|
# Templates for starting other projects quickly
|
||||||
|
|||||||
@@ -12,9 +12,19 @@ rec {
|
|||||||
|
|
||||||
nmasur.profiles = {
|
nmasur.profiles = {
|
||||||
base.enable = true;
|
base.enable = true;
|
||||||
work.enable = true;
|
# work.enable = true;
|
||||||
extra.enable = true;
|
# extra.enable = true;
|
||||||
gaming.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" = {
|
home-manager.users."Noah.Masur" = {
|
||||||
@@ -26,10 +36,13 @@ rec {
|
|||||||
nmasur.profiles = {
|
nmasur.profiles = {
|
||||||
common.enable = true;
|
common.enable = true;
|
||||||
darwin-base.enable = true;
|
darwin-base.enable = true;
|
||||||
|
darwin-gaming.enable = true;
|
||||||
|
llm-development.enable = true;
|
||||||
power-user.enable = true;
|
power-user.enable = true;
|
||||||
work.enable = true;
|
work.enable = true;
|
||||||
experimental.enable = true;
|
experimental.enable = true;
|
||||||
};
|
};
|
||||||
|
nmasur.presets.services.mbsync.user = "noah";
|
||||||
nmasur.presets.programs.git-work.work = {
|
nmasur.presets.programs.git-work.work = {
|
||||||
name = "Noah-Masur_1701";
|
name = "Noah-Masur_1701";
|
||||||
email = "${nmasur.settings.username}@take2games.com";
|
email = "${nmasur.settings.username}@take2games.com";
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ rec {
|
|||||||
nmasur.profiles = {
|
nmasur.profiles = {
|
||||||
common.enable = true;
|
common.enable = true;
|
||||||
linux-base.enable = true;
|
linux-base.enable = true;
|
||||||
|
llm-development.enable = true;
|
||||||
power-user.enable = true;
|
power-user.enable = true;
|
||||||
};
|
};
|
||||||
nmasur.presets.programs.helix.enable = true;
|
nmasur.presets.programs.helix.enable = true;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ rec {
|
|||||||
nmasur.profiles = {
|
nmasur.profiles = {
|
||||||
common.enable = true;
|
common.enable = true;
|
||||||
linux-base.enable = true;
|
linux-base.enable = true;
|
||||||
|
llm-development.enable = true;
|
||||||
power-user.enable = true;
|
power-user.enable = true;
|
||||||
};
|
};
|
||||||
home.stateVersion = "23.05";
|
home.stateVersion = "23.05";
|
||||||
@@ -33,6 +34,9 @@ rec {
|
|||||||
|
|
||||||
system.stateVersion = "23.05";
|
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
|
# Not sure what's necessary but too afraid to remove anything
|
||||||
boot.initrd.availableKernelModules = [
|
boot.initrd.availableKernelModules = [
|
||||||
"xhci_pci"
|
"xhci_pci"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ rec {
|
|||||||
linux-base.enable = true;
|
linux-base.enable = true;
|
||||||
linux-gui.enable = true;
|
linux-gui.enable = true;
|
||||||
linux-gaming.enable = true;
|
linux-gaming.enable = true;
|
||||||
|
llm-development.enable = true;
|
||||||
power-user.enable = true;
|
power-user.enable = true;
|
||||||
developer.enable = true;
|
developer.enable = true;
|
||||||
experimental.enable = true;
|
experimental.enable = true;
|
||||||
@@ -51,7 +52,7 @@ rec {
|
|||||||
|
|
||||||
# Graphics and VMs
|
# Graphics and VMs
|
||||||
boot.initrd.kernelModules = [ "amdgpu" ];
|
boot.initrd.kernelModules = [ "amdgpu" ];
|
||||||
boot.kernelModules = [ "kvm-amd" ];
|
boot.kernelModules = [ "kvm-amd" "it87" ];
|
||||||
services.xserver.videoDrivers = [ "amdgpu" ];
|
services.xserver.videoDrivers = [ "amdgpu" ];
|
||||||
|
|
||||||
# Required binary blobs to boot on this machine
|
# Required binary blobs to boot on this machine
|
||||||
@@ -68,16 +69,16 @@ rec {
|
|||||||
hardware.fancontrol.config = ''
|
hardware.fancontrol.config = ''
|
||||||
# Configuration file generated by pwmconfig, changes will be lost
|
# Configuration file generated by pwmconfig, changes will be lost
|
||||||
INTERVAL=10
|
INTERVAL=10
|
||||||
DEVPATH=hwmon0=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
|
DEVPATH=hwmon1=devices/pci0000:00/0000:00:03.1/0000:06:00.0/0000:07:00.0/0000:08:00.0
|
||||||
DEVNAME=hwmon0=amdgpu
|
DEVNAME=hwmon1=amdgpu
|
||||||
FCTEMPS=hwmon0/pwm1=hwmon0/temp1_input
|
FCTEMPS=hwmon1/pwm1=hwmon1/temp1_input
|
||||||
FCFANS= hwmon0/pwm1=hwmon0/fan1_input
|
FCFANS= hwmon1/pwm1=hwmon1/fan1_input
|
||||||
MINTEMP=hwmon0/pwm1=50
|
MINTEMP=hwmon1/pwm1=50
|
||||||
MAXTEMP=hwmon0/pwm1=70
|
MAXTEMP=hwmon1/pwm1=70
|
||||||
MINSTART=hwmon0/pwm1=100
|
MINSTART=hwmon1/pwm1=100
|
||||||
MINSTOP=hwmon0/pwm1=10
|
MINSTOP=hwmon1/pwm1=10
|
||||||
MINPWM=hwmon0/pwm1=10
|
MINPWM=hwmon1/pwm1=10
|
||||||
MAXPWM=hwmon0/pwm1=240
|
MAXPWM=hwmon1/pwm1=240
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# File systems must be declared in order to boot
|
# File systems must be declared in order to boot
|
||||||
|
|||||||
+39
-19
@@ -66,14 +66,16 @@ lib
|
|||||||
overlays = [
|
overlays = [
|
||||||
inputs.nur.overlays.default
|
inputs.nur.overlays.default
|
||||||
inputs.nix2vim.overlay
|
inputs.nix2vim.overlay
|
||||||
inputs.zellij-switch.overlays.default
|
(final: prev: {
|
||||||
inputs.helix.overlays.default
|
zellij-switch = inputs.zellij-switch.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
] ++ (importOverlays ../overlays);
|
})
|
||||||
|
# inputs.helix.overlays.default
|
||||||
|
]
|
||||||
|
++ (importOverlays ../overlays);
|
||||||
|
|
||||||
# System types to support.
|
# System types to support.
|
||||||
supportedSystems = [
|
supportedSystems = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
"x86_64-darwin"
|
|
||||||
"aarch64-linux"
|
"aarch64-linux"
|
||||||
"aarch64-darwin"
|
"aarch64-darwin"
|
||||||
];
|
];
|
||||||
@@ -97,7 +99,14 @@ lib
|
|||||||
system:
|
system:
|
||||||
import inputs.nixpkgs {
|
import inputs.nixpkgs {
|
||||||
inherit system overlays;
|
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;
|
config.allowUnfree = true;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -130,7 +139,8 @@ lib
|
|||||||
];
|
];
|
||||||
extraSpecialArgs = {
|
extraSpecialArgs = {
|
||||||
inherit colorscheme;
|
inherit colorscheme;
|
||||||
} // specialArgs;
|
}
|
||||||
|
// specialArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
buildNixos =
|
buildNixos =
|
||||||
@@ -144,16 +154,20 @@ lib
|
|||||||
pkgs = pkgsBySystem.${system};
|
pkgs = pkgsBySystem.${system};
|
||||||
modules = [
|
modules = [
|
||||||
inputs.home-manager.nixosModules.home-manager
|
inputs.home-manager.nixosModules.home-manager
|
||||||
|
inputs.nix-index-database.nixosModules.default
|
||||||
inputs.disko.nixosModules.disko
|
inputs.disko.nixosModules.disko
|
||||||
inputs.wsl.nixosModules.wsl
|
inputs.wsl.nixosModules.wsl
|
||||||
{ imports = (nixFiles ../platforms/nixos); }
|
{ imports = (nixFiles ../platforms/nixos); }
|
||||||
module
|
module
|
||||||
|
# (builtins.removeAttrs module [ "home-manager" ])
|
||||||
{
|
{
|
||||||
home-manager = {
|
home-manager = {
|
||||||
extraSpecialArgs = {
|
extraSpecialArgs = {
|
||||||
inherit colorscheme;
|
inherit colorscheme;
|
||||||
} // specialArgs;
|
}
|
||||||
} // homeModule.home-manager;
|
// specialArgs;
|
||||||
|
}
|
||||||
|
// homeModule.home-manager;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
@@ -168,19 +182,21 @@ lib
|
|||||||
inherit system specialArgs;
|
inherit system specialArgs;
|
||||||
modules = [
|
modules = [
|
||||||
inputs.home-manager.darwinModules.home-manager
|
inputs.home-manager.darwinModules.home-manager
|
||||||
|
inputs.nix-index-database.darwinModules.nix-index
|
||||||
inputs.mac-app-util.darwinModules.default
|
inputs.mac-app-util.darwinModules.default
|
||||||
{
|
{
|
||||||
imports = (nixFiles ../platforms/nix-darwin);
|
imports = (nixFiles ../platforms/nix-darwin);
|
||||||
nixpkgs.pkgs = pkgsBySystem.${system};
|
nixpkgs.pkgs = pkgsBySystem.${system};
|
||||||
}
|
}
|
||||||
module
|
# Home Manager is intentionally NOT activated here. It is managed
|
||||||
{
|
# standalone via `nh home switch` (see homeConfigurations, extracted
|
||||||
home-manager = {
|
# from the host module's `home-manager.users`). Strip that attr so
|
||||||
extraSpecialArgs = {
|
# darwin-rebuild doesn't also activate a second, divergent HM
|
||||||
inherit colorscheme;
|
# generation with different store paths (useUserPackages puts packages
|
||||||
} // specialArgs;
|
# in /etc/profiles/per-user vs. ~/.nix-profile standalone) -- that
|
||||||
} // homeModule.home-manager;
|
# 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};
|
pkgs = pkgsBySystem.${system};
|
||||||
modules = [
|
modules = [
|
||||||
inputs.home-manager.nixosModules.home-manager
|
inputs.home-manager.nixosModules.home-manager
|
||||||
|
inputs.nix-index-database.nixosModules.default
|
||||||
inputs.disko.nixosModules.disko
|
inputs.disko.nixosModules.disko
|
||||||
inputs.wsl.nixosModules.wsl
|
inputs.wsl.nixosModules.wsl
|
||||||
{
|
{
|
||||||
@@ -222,12 +239,15 @@ lib
|
|||||||
home-manager = {
|
home-manager = {
|
||||||
extraSpecialArgs = {
|
extraSpecialArgs = {
|
||||||
inherit colorscheme;
|
inherit colorscheme;
|
||||||
} // specialArgs;
|
}
|
||||||
} // homeModule.home-manager;
|
// specialArgs;
|
||||||
|
}
|
||||||
|
// homeModule.home-manager;
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
specialArgs = {
|
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 ]
|
# [ package1.drv package2.drv ]
|
||||||
(builtins.map (name: prev.callPackage name { }))
|
(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
|
# Convert the list to an attrset
|
||||||
# { package1 = package1.drv, package2 = package2.drv }
|
# { package1 = package1.drv, package2 = package2.drv }
|
||||||
listToAttrsByPnameOrName
|
listToAttrsByPnameOrName
|
||||||
|
|||||||
+2
-1
@@ -4,7 +4,8 @@
|
|||||||
inputs: _final: prev: {
|
inputs: _final: prev: {
|
||||||
# Provides `pkgs.stable`.
|
# Provides `pkgs.stable`.
|
||||||
stable = import inputs.nixpkgs-stable {
|
stable = import inputs.nixpkgs-stable {
|
||||||
inherit (prev) system config;
|
system = prev.stdenv.hostPlatform.system;
|
||||||
|
config = { };
|
||||||
overlays = [
|
overlays = [
|
||||||
# inputs.self.overlays.vim-plugins
|
# inputs.self.overlays.vim-plugins
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
inputs: _final: prev: {
|
inputs: _final: prev: {
|
||||||
# Provides `pkgs.unstable`.
|
# Provides `pkgs.unstable`.
|
||||||
unstable = import inputs.nixpkgs {
|
unstable = import inputs.nixpkgs {
|
||||||
inherit (prev) system config;
|
system = prev.stdenv.hostPlatform.system;
|
||||||
|
inherit (prev) config;
|
||||||
overlays = [
|
overlays = [
|
||||||
# inputs.self.overlays.vim-plugins
|
# 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";
|
lua.command = "${pkgs.stylua}/bin/stylua";
|
||||||
black.command = "${pkgs.black}/bin/black";
|
black.command = "${pkgs.black}/bin/black";
|
||||||
fish_indent.command = "${pkgs.fish}/bin/fish_indent";
|
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";
|
rustfmt.command = "${pkgs.rustfmt}/bin/rustfmt";
|
||||||
shfmt = {
|
shfmt = {
|
||||||
command = "${pkgs.shfmt}/bin/shfmt";
|
command = "${pkgs.shfmt}/bin/shfmt";
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
|
|
||||||
plugins = [
|
plugins = [
|
||||||
(pkgs.vimPlugins.nvim-treesitter.withPlugins (_plugins: [
|
(pkgs.vimPlugins.nvim-treesitter.withPlugins (_plugins: [
|
||||||
pkgs.nmasur.ini-grammar
|
pkgs.nmasur.tree-sitter-ini
|
||||||
pkgs.nmasur.puppet-grammar
|
pkgs.nmasur.tree-sitter-puppet
|
||||||
pkgs.nmasur.rasi-grammar
|
pkgs.nmasur.tree-sitter-rasi
|
||||||
pkgs.nmasur.vimdoc-grammar
|
pkgs.nmasur.tree-sitter-vimdoc
|
||||||
pkgs.tree-sitter-grammars.tree-sitter-bash
|
pkgs.tree-sitter-grammars.tree-sitter-bash
|
||||||
pkgs.tree-sitter-grammars.tree-sitter-c
|
pkgs.tree-sitter-grammars.tree-sitter-c
|
||||||
pkgs.tree-sitter-grammars.tree-sitter-fish
|
pkgs.tree-sitter-grammars.tree-sitter-fish
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
pkgs.tree-sitter-grammars.tree-sitter-yaml
|
pkgs.tree-sitter-grammars.tree-sitter-yaml
|
||||||
]))
|
]))
|
||||||
pkgs.vimPlugins.vim-matchup # Better % jumping in languages
|
pkgs.vimPlugins.vim-matchup # Better % jumping in languages
|
||||||
pkgs.vimPlugins.playground # Tree-sitter experimenting
|
|
||||||
pkgs.vimPlugins.nginx-vim
|
pkgs.vimPlugins.nginx-vim
|
||||||
pkgs.vimPlugins.vim-helm
|
pkgs.vimPlugins.vim-helm
|
||||||
# pkgs.vimPlugins.hmts-nvim # Tree-sitter injections for home-manager
|
# 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.
|
# Fix: Volnoti error: 'volnoti' has been removed due to lack of maintenance upstream.
|
||||||
|
|
||||||
{ pkgs, lib, ... }:
|
{ pkgs, lib, ... }:
|
||||||
|
if !pkgs.stdenv.hostPlatform.isLinux then
|
||||||
|
null
|
||||||
|
else
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
pname = "volnoti";
|
pname = "volnoti";
|
||||||
version = "2013-09-23";
|
version = "2013-09-23";
|
||||||
@@ -27,7 +30,7 @@ pkgs.stdenv.mkDerivation {
|
|||||||
dbus
|
dbus
|
||||||
gdk-pixbuf
|
gdk-pixbuf
|
||||||
glib
|
glib
|
||||||
xorg.libX11
|
libx11
|
||||||
gtk2
|
gtk2
|
||||||
dbus-glib
|
dbus-glib
|
||||||
librsvg
|
librsvg
|
||||||
|
|||||||
+4
-15
@@ -2,21 +2,10 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
fetchFromGitHub,
|
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
|
|
||||||
# Maintain a static version so that the plugin hash doesn't keep breaking
|
pkgs.caddy.withPlugins {
|
||||||
(pkgs.caddy.overrideAttrs rec {
|
plugins = [ "github.com/caddy-dns/cloudflare@v0.2.1" ];
|
||||||
version = "2.10.2";
|
hash = "sha256-HuVBmiT3kD6RrDejQ6SnjCN8f7pFdZlGtZFbEf47bks=";
|
||||||
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=";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
drf-access-policy
|
||||||
# psycopg-binary
|
# 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,
|
lib,
|
||||||
fetchFromGitHub,
|
fetchFromGitHub,
|
||||||
nodejs_20,
|
nodejs_22,
|
||||||
buildNpmPackage,
|
buildNpmPackage,
|
||||||
nodePackages,
|
typescript,
|
||||||
|
node-gyp,
|
||||||
python3,
|
python3,
|
||||||
gcc,
|
gcc,
|
||||||
gnumake,
|
gnumake,
|
||||||
@@ -29,10 +30,10 @@ buildNpmPackage (finalAttrs: rec {
|
|||||||
npmDepsHash = "sha256-N8xqRYFelolNGTEhG22M7KJ7B5U/uW7o+/XfLF8rHMg=";
|
npmDepsHash = "sha256-N8xqRYFelolNGTEhG22M7KJ7B5U/uW7o+/XfLF8rHMg=";
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
nodejs_20
|
nodejs_22
|
||||||
nodePackages.typescript
|
typescript
|
||||||
python3
|
python3
|
||||||
nodePackages.node-gyp
|
node-gyp
|
||||||
gcc
|
gcc
|
||||||
gnumake
|
gnumake
|
||||||
];
|
];
|
||||||
@@ -53,8 +54,8 @@ buildNpmPackage (finalAttrs: rec {
|
|||||||
# export npm_config_build_from_source=true
|
# export npm_config_build_from_source=true
|
||||||
# export npm_config_unsafe_perm=true
|
# export npm_config_unsafe_perm=true
|
||||||
# export BINARY_SITE=none
|
# export BINARY_SITE=none
|
||||||
# export PATH=${nodePackages.node-gyp}/bin:$PATH
|
# export PATH=${node-gyp}/bin:$PATH
|
||||||
# export npm_config_node_gyp=${nodePackages.node-gyp}/bin/node-gyp
|
# export npm_config_node_gyp=${node-gyp}/bin/node-gyp
|
||||||
|
|
||||||
# npm rebuild better-sqlite3 --build-from-source --verbose
|
# npm rebuild better-sqlite3 --build-from-source --verbose
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ buildNpmPackage (finalAttrs: rec {
|
|||||||
installPhase = ''
|
installPhase = ''
|
||||||
mkdir -p $out/{bin,lib}
|
mkdir -p $out/{bin,lib}
|
||||||
cp -r . $out/lib/prometheus-actual-exporter
|
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"
|
--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_build_from_source=true
|
||||||
export npm_config_unsafe_perm=true
|
export npm_config_unsafe_perm=true
|
||||||
export BINARY_SITE=none
|
export BINARY_SITE=none
|
||||||
export PATH=${nodePackages.node-gyp}/bin:$PATH
|
export PATH=${node-gyp}/bin:$PATH
|
||||||
export npm_config_node_gyp=${nodePackages.node-gyp}/bin/node-gyp
|
export npm_config_node_gyp=${node-gyp}/bin/node-gyp
|
||||||
|
|
||||||
sed -i '/"install"/d' node_modules/better-sqlite3/package.json
|
sed -i '/"install"/d' node_modules/better-sqlite3/package.json
|
||||||
rm -f node_modules/better-sqlite3/build/Release/better_sqlite3.node || true
|
rm -f node_modules/better-sqlite3/build/Release/better_sqlite3.node || true
|
||||||
|
|||||||
@@ -1,39 +1,42 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
|
stdenv,
|
||||||
buildDotnetModule,
|
buildDotnetModule,
|
||||||
fetchFromGitHub,
|
fetchFromGitHub,
|
||||||
dotnetCorePackages,
|
dotnetCorePackages,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
buildDotnetModule rec {
|
if !stdenv.hostPlatform.isLinux then
|
||||||
pname = "slsk-batchdl";
|
null
|
||||||
version = "2.4.7";
|
else
|
||||||
|
buildDotnetModule rec {
|
||||||
|
pname = "slsk-batchdl";
|
||||||
|
version = "2.4.7";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "fiso64";
|
owner = "fiso64";
|
||||||
repo = "slsk-batchdl";
|
repo = "slsk-batchdl";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
sha256 = "sha256-P7V7YJUA1bkfp13Glb1Q+NJ7iTya/xgO1TM88z1Nddc=";
|
sha256 = "sha256-P7V7YJUA1bkfp13Glb1Q+NJ7iTya/xgO1TM88z1Nddc=";
|
||||||
};
|
};
|
||||||
|
|
||||||
projectFile = "slsk-batchdl/slsk-batchdl.csproj";
|
projectFile = "slsk-batchdl/slsk-batchdl.csproj";
|
||||||
nugetDeps = ./nuget-deps.nix;
|
nugetDeps = ./nuget-deps.nix;
|
||||||
|
|
||||||
dotnet-sdk = dotnetCorePackages.sdk_8_0;
|
dotnet-sdk = dotnetCorePackages.sdk_8_0;
|
||||||
dotnet-runtime = dotnetCorePackages.runtime_8_0;
|
dotnet-runtime = dotnetCorePackages.runtime_8_0;
|
||||||
|
|
||||||
# Patch the project file to use .NET 8
|
postPatch = ''
|
||||||
postPatch = ''
|
substituteInPlace slsk-batchdl/slsk-batchdl.csproj \
|
||||||
substituteInPlace slsk-batchdl/slsk-batchdl.csproj \
|
--replace-fail "net6.0" "net8.0"
|
||||||
--replace-fail "net6.0" "net8.0"
|
'';
|
||||||
'';
|
|
||||||
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
description = "A batch downloader for Soulseek";
|
description = "A batch downloader for Soulseek";
|
||||||
homepage = "https://github.com/fiso64/slsk-batchdl";
|
homepage = "https://github.com/fiso64/slsk-batchdl";
|
||||||
platforms = platforms.linux;
|
platforms = platforms.linux;
|
||||||
mainProgram = "slsk-batchdl";
|
mainProgram = "slsk-batchdl";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
# Specify AWS_PROFILE and AWS_REGION before running this script
|
# Specify AWS_PROFILE and AWS_REGION before running this script
|
||||||
|
|
||||||
aws ec2 describe-instances \
|
aws ec2 describe-instances \
|
||||||
--filters "Name=instance-state-name,Values=running" |
|
--filters "Name=instance-state-name,Values=running" |
|
||||||
jq -r \
|
jq -r \
|
||||||
'.Reservations[]
|
'.Reservations[]
|
||||||
| .Instances[]
|
| .Instances[]
|
||||||
| .InstanceId + " - " +
|
| .InstanceId + " - " +
|
||||||
(.PrivateIpAddress // "n/a") + " - " +
|
(.PrivateIpAddress // "n/a") + " - " +
|
||||||
(.PublicIpAddress // "n/a") + " - " +
|
(.PublicIpAddress // "n/a") + " - " +
|
||||||
(.Tags // [] | from_entries | .Name // "n/a")' |
|
(.Tags // [] | from_entries | .Name // "n/a")' |
|
||||||
fzf \
|
fzf \
|
||||||
--height 100% \
|
--height 100% \
|
||||||
--layout reverse \
|
--layout reverse \
|
||||||
--header $'Press Enter to start SSM session\nInstance ID - Private IP - Public IP - Name' \
|
--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]'" \
|
--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))"
|
--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; [
|
buildInputs = with pkgs; [
|
||||||
git
|
git
|
||||||
stylua
|
stylua
|
||||||
nixfmt-rfc-style
|
nixfmt
|
||||||
shfmt
|
shfmt
|
||||||
shellcheck
|
shellcheck
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,44 +1,47 @@
|
|||||||
{ pkgs, ... }:
|
{ pkgs, lib, ... }:
|
||||||
|
|
||||||
# Inspired by https://github.com/cleverca22/nix-tests/blob/master/kexec/justdoit.nix
|
# 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!
|
# This script will partition and format drives; use at your own risk!
|
||||||
|
|
||||||
pkgs.writeShellScriptBin "installer" ''
|
if !pkgs.stdenv.hostPlatform.isLinux then
|
||||||
set -e
|
null
|
||||||
|
else
|
||||||
|
pkgs.writeShellScriptBin "installer" ''
|
||||||
|
set -e
|
||||||
|
|
||||||
DISK=$1
|
DISK=$1
|
||||||
FLAKE=$2
|
FLAKE=$2
|
||||||
PARTITION_PREFIX=""
|
PARTITION_PREFIX=""
|
||||||
|
|
||||||
if [ -z "$DISK" ] || [ -z "$FLAKE" ]; then
|
if [ -z "$DISK" ] || [ -z "$FLAKE" ]; then
|
||||||
${pkgs.gum}/bin/gum style --width 50 --margin "1 2" --padding "2 4" \
|
${pkgs.gum}/bin/gum style --width 50 --margin "1 2" --padding "2 4" \
|
||||||
--foreground "#fb4934" \
|
--foreground "#fb4934" \
|
||||||
"Missing required parameter." \
|
"Missing required parameter." \
|
||||||
"Usage: installer -- <disk> <host>" \
|
"Usage: installer -- <disk> <host>" \
|
||||||
"Example: installer -- nvme0n1 tempest" \
|
"Example: installer -- nvme0n1 tempest" \
|
||||||
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 tempest"
|
"Flake example: nix run github:nmasur/dotfiles#installer -- nvme0n1 tempest"
|
||||||
echo "(exiting)"
|
echo "(exiting)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$DISK" in nvme*)
|
case "$DISK" in nvme*)
|
||||||
PARTITION_PREFIX="p"
|
PARTITION_PREFIX="p"
|
||||||
esac
|
esac
|
||||||
|
|
||||||
${pkgs.gum}/bin/gum confirm \
|
${pkgs.gum}/bin/gum confirm \
|
||||||
"This will ERASE ALL DATA on the disk /dev/''${DISK}. Are you sure you want to continue?" \
|
"This will ERASE ALL DATA on the disk /dev/''${DISK}. Are you sure you want to continue?" \
|
||||||
--default=false
|
--default=false
|
||||||
|
|
||||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mklabel gpt
|
${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 primary 512MiB 100%
|
||||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart ESP fat32 1MiB 512MiB
|
${pkgs.parted}/bin/parted /dev/''${DISK} -- mkpart ESP fat32 1MiB 512MiB
|
||||||
${pkgs.parted}/bin/parted /dev/''${DISK} -- set 3 esp on
|
${pkgs.parted}/bin/parted /dev/''${DISK} -- set 3 esp on
|
||||||
mkfs.ext4 -L nixos /dev/''${DISK}''${PARTITION_PREFIX}1
|
mkfs.ext4 -L nixos /dev/''${DISK}''${PARTITION_PREFIX}1
|
||||||
mkfs.fat -F 32 -n boot /dev/''${DISK}''${PARTITION_PREFIX}2
|
mkfs.fat -F 32 -n boot /dev/''${DISK}''${PARTITION_PREFIX}2
|
||||||
|
|
||||||
mount /dev/disk/by-label/nixos /mnt
|
mount /dev/disk/by-label/nixos /mnt
|
||||||
mkdir --parents /mnt/boot
|
mkdir --parents /mnt/boot
|
||||||
mount /dev/disk/by-label/boot /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, ... }:
|
||||||
|
|
||||||
pkgs.writeShellScriptBin "rebuild" ''
|
pkgs.writeShellScriptBin "rebuild" ''
|
||||||
echo ${pkgs.system}
|
echo ${pkgs.stdenv.hostPlatform.system}
|
||||||
SYSTEM=${if pkgs.stdenv.isDarwin then "darwin" else "linux"}
|
SYSTEM=${if pkgs.stdenv.hostPlatform.isDarwin then "darwin" else "linux"}
|
||||||
if [ "$SYSTEM" == "darwin" ]; then
|
if [ "$SYSTEM" == "darwin" ]; then
|
||||||
sudo darwin-rebuild switch --flake ${builtins.toString ../../../../.}
|
sudo darwin-rebuild switch --flake ${builtins.toString ../../../../.}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -17,23 +17,27 @@ in
|
|||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs.nerd-fonts.victor-mono # Used for Vim and Terminal
|
pkgs.nerd-fonts.victor-mono # Used for Vim and Terminal
|
||||||
pkgs.nerd-fonts.hack # For Polybar, Rofi
|
pkgs.nerd-fonts.hack # For Polybar, Rofi
|
||||||
|
|
||||||
|
# Maple Mono NF (Ligature unhinted)
|
||||||
|
pkgs.maple-mono.NF-unhinted
|
||||||
];
|
];
|
||||||
fonts.fontconfig = {
|
fonts.fontconfig = {
|
||||||
enable = true;
|
enable = true;
|
||||||
defaultFonts.monospace = [ "Victor Mono" ];
|
defaultFonts.monospace = [ "Maple Mono NF" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
xsession.windowManager.i3.config.fonts = {
|
xsession.windowManager.i3.config.fonts = {
|
||||||
names = [ "pango:Victor Mono" ];
|
# names = [ "pango:Victor Mono" ];
|
||||||
|
names = [ "pango:Maple Mono" ];
|
||||||
# style = "Regular";
|
# style = "Regular";
|
||||||
# size = 11.0;
|
# size = 11.0;
|
||||||
};
|
};
|
||||||
services.polybar.config."bar/main".font-0 = "Hack Nerd Font:size=10;2";
|
services.polybar.config."bar/main".font-0 = "Hack Nerd Font:size=10;2";
|
||||||
programs.rofi.font = "Hack Nerd Font 14";
|
programs.rofi.font = "Hack Nerd Font 14";
|
||||||
programs.alacritty.settings.font.normal.family = "VictorMono";
|
programs.alacritty.settings.font.normal.family = "Maple Mono NF";
|
||||||
programs.kitty.font.name = "VictorMono Nerd Font Mono";
|
programs.kitty.font.name = "Maple Mono NF";
|
||||||
nmasur.presets.programs.wezterm.font = "VictorMono Nerd Font Mono";
|
nmasur.presets.programs.wezterm.font = "Maple Mono NF";
|
||||||
programs.ghostty.settings.font-family = "VictorMono Nerd Font Mono";
|
programs.ghostty.settings.font-family = "Maple Mono NF";
|
||||||
services.dunst.settings.global.font = "Hack Nerd Font 14";
|
services.dunst.settings.global.font = "Hack Nerd Font 14";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ in
|
|||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
# Cursor
|
# Cursor
|
||||||
home.pointerCursor = {
|
home.pointerCursor = {
|
||||||
|
enable = true;
|
||||||
name = "Adwaita";
|
name = "Adwaita";
|
||||||
package = pkgs.adwaita-icon-theme;
|
package = pkgs.adwaita-icon-theme;
|
||||||
size = 24;
|
size = 24;
|
||||||
@@ -48,6 +49,7 @@ in
|
|||||||
gtk4.extraConfig = {
|
gtk4.extraConfig = {
|
||||||
gtk-application-prefer-dark-theme = config.theme.mode == "dark";
|
gtk-application-prefer-dark-theme = config.theme.mode == "dark";
|
||||||
};
|
};
|
||||||
|
gtk4.theme = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,16 +18,17 @@ in
|
|||||||
"1password"
|
"1password"
|
||||||
"_1password-gui"
|
"_1password-gui"
|
||||||
"1password-cli"
|
"1password-cli"
|
||||||
"onepassword-password-manager" # Firefox extension
|
# "onepassword-password-manager" # Firefox extension
|
||||||
];
|
];
|
||||||
home.packages = [
|
home.packages = [
|
||||||
pkgs._1password-cli
|
pkgs._1password-cli
|
||||||
] ++ (if pkgs.stdenv.isLinux then [ pkgs._1password-gui ] else [ ]);
|
]
|
||||||
|
++ (if pkgs.stdenv.hostPlatform.isLinux then [ pkgs._1password-gui ] else [ ]);
|
||||||
|
|
||||||
# Firefox extension
|
# # Firefox extension
|
||||||
programs.firefox.profiles.default.extensions.packages = [
|
# programs.firefox.profiles.default.extensions.packages = [
|
||||||
pkgs.nur.repos.rycee.firefox-addons.onepassword-password-manager
|
# pkgs.nur.repos.rycee.firefox-addons.onepassword-password-manager
|
||||||
];
|
# ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# # https://1password.community/discussion/135462/firefox-extension-does-not-connect-to-linux-app
|
# # https://1password.community/discussion/135462/firefox-extension-does-not-connect-to-linux-app
|
||||||
|
|||||||
@@ -174,8 +174,10 @@ in
|
|||||||
filters = {
|
filters = {
|
||||||
"text/plain" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
|
"text/plain" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
|
||||||
"text/calendar" = "${pkgs.gawk}/bin/awk -f ${pkgs.aerc}/libexec/aerc/filters/calendar";
|
"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" =
|
"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/*" =
|
# "text/*" =
|
||||||
# ''${pkgs.bat}/bin/bat -fP --file-name="$AERC_FILENAME "'';
|
# ''${pkgs.bat}/bin/bat -fP --file-name="$AERC_FILENAME "'';
|
||||||
"message/delivery-status" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
|
"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";
|
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" =
|
"${config.xsession.windowManager.i3.config.modifier}+Shift+e" =
|
||||||
let
|
let
|
||||||
terminal = config.nmasur.presets.services.i3.terminal;
|
terminal = config.nmasur.presets.services.i3.terminal;
|
||||||
@@ -212,7 +217,7 @@ in
|
|||||||
if terminal == pkgs.wezterm then
|
if terminal == pkgs.wezterm then
|
||||||
"start --class com.noah.aerc -- aerc"
|
"start --class com.noah.aerc -- aerc"
|
||||||
else
|
else
|
||||||
"--class=com.noah.aerc --command=aerc";
|
"--class=com.noah.aerc -e aerc";
|
||||||
in
|
in
|
||||||
"exec ${
|
"exec ${
|
||||||
# Don't name the script `aerc` or it will affect grep
|
# Don't name the script `aerc` or it will affect grep
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ in
|
|||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
accounts.calendar.accounts.default = {
|
accounts.calendar.accounts.default = {
|
||||||
basePath = "other/calendars"; # Where to save calendars in ~ directory
|
|
||||||
name = "personal";
|
name = "personal";
|
||||||
local.type = "filesystem";
|
local.type = "filesystem";
|
||||||
primary = true;
|
primary = true;
|
||||||
|
|||||||
@@ -14,9 +14,14 @@ in
|
|||||||
options.nmasur.presets.programs.calibre.enable = lib.mkEnableOption "Calibre e-book manager";
|
options.nmasur.presets.programs.calibre.enable = lib.mkEnableOption "Calibre e-book manager";
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
home.packages = [ pkgs.calibre ];
|
home.packages = lib.mkIf pkgs.stdenv.hostPlatform.isLinux [ pkgs.calibre ];
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
CALIBRE_USE_DARK_PALETTE = 1;
|
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";
|
ca = "cargo";
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = with pkgs; [
|
home.packages = [
|
||||||
gcc
|
pkgs.gcc
|
||||||
rustc
|
pkgs.rustc
|
||||||
cargo
|
pkgs.cargo
|
||||||
cargo-watch
|
pkgs.stable.cargo-watch
|
||||||
clippy
|
pkgs.clippy
|
||||||
rustfmt
|
pkgs.rustfmt
|
||||||
pkg-config
|
pkgs.pkg-config
|
||||||
openssl
|
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 {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
# Always make the dotfiles directory considered safe for git and direnv
|
# 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 ];
|
programs.direnv.config.whitelist.prefix = [ cfg.path ];
|
||||||
|
|
||||||
home.activation = {
|
home.activation = {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ in
|
|||||||
programs.firefox = {
|
programs.firefox = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = pkgs.firefox;
|
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 = {
|
profiles.default = {
|
||||||
id = 0;
|
id = 0;
|
||||||
name = "default";
|
name = "default";
|
||||||
@@ -46,6 +48,8 @@ in
|
|||||||
ublacklist
|
ublacklist
|
||||||
vimium
|
vimium
|
||||||
wappalyzer # TODO: only for work profile
|
wappalyzer # TODO: only for work profile
|
||||||
|
# pkgs.nmasur.firefox-history-exporter
|
||||||
|
# copy-as-markdown
|
||||||
# saml-tracer
|
# saml-tracer
|
||||||
# text-fragment
|
# text-fragment
|
||||||
];
|
];
|
||||||
@@ -53,7 +57,7 @@ in
|
|||||||
"app.update.auto" = false;
|
"app.update.auto" = false;
|
||||||
"browser.aboutConfig.showWarning" = false;
|
"browser.aboutConfig.showWarning" = false;
|
||||||
"browser.warnOnQuit" = 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.theme.dark-private-windows" = true;
|
||||||
"browser.toolbars.bookmarks.visibility" = false;
|
"browser.toolbars.bookmarks.visibility" = false;
|
||||||
"browser.startup.page" = 3; # Restore previous session
|
"browser.startup.page" = 3; # Restore previous session
|
||||||
@@ -61,6 +65,7 @@ in
|
|||||||
"trailhead.firstrun.didSeeAboutWelcome" = true; # Disable welcome splash
|
"trailhead.firstrun.didSeeAboutWelcome" = true; # Disable welcome splash
|
||||||
"dom.forms.autocomplete.formautofill" = false; # Disable autofill
|
"dom.forms.autocomplete.formautofill" = false; # Disable autofill
|
||||||
"extensions.formautofill.creditCards.enabled" = false; # Disable credit cards
|
"extensions.formautofill.creditCards.enabled" = false; # Disable credit cards
|
||||||
|
"extensions.autoDisableScopes" = false; # Enable extensions automatically
|
||||||
"dom.payments.defaults.saveAddress" = false; # Disable address save
|
"dom.payments.defaults.saveAddress" = false; # Disable address save
|
||||||
"general.autoScroll" = true; # Drag middle-mouse to scroll
|
"general.autoScroll" = true; # Drag middle-mouse to scroll
|
||||||
"services.sync.prefs.sync.general.autoScroll" = false; # Prevent disabling autoscroll
|
"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 ${
|
"${config.xsession.windowManager.i3.config.modifier}+Shift+b" = "exec ${
|
||||||
# Don't name the script `firefox` or it will affect grep
|
# Don't name the script `firefox` or it will affect grep
|
||||||
builtins.toString (
|
toString (
|
||||||
pkgs.writeShellScript "focus-ff.sh" ''
|
pkgs.writeShellScript "focus-ff.sh" ''
|
||||||
count=$(ps aux | grep -c firefox)
|
count=$(ps aux | grep -c firefox)
|
||||||
if [ "$count" -eq 1 ]; then
|
if [ "$count" -eq 1 ]; then
|
||||||
|
|||||||
@@ -13,13 +13,35 @@ in
|
|||||||
|
|
||||||
options.nmasur.presets.programs.fish-darwin.enable = lib.mkEnableOption {
|
options.nmasur.presets.programs.fish-darwin.enable = lib.mkEnableOption {
|
||||||
description = "Fish macOS options";
|
description = "Fish macOS options";
|
||||||
default = config.nmasur.presets.programs.fish && pkgs.stdenv.isDarwin;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
programs.fish.shellAbbrs = {
|
# Default shell setting doesn't work
|
||||||
# Shortcut to edit hosts file
|
home.sessionVariables = {
|
||||||
hosts = "sudo nvim /etc/hosts";
|
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";
|
dr = "docker run --rm -it";
|
||||||
db = "docker build . -t";
|
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 = {
|
programs.ghostty = {
|
||||||
enable = true;
|
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;
|
enableBashIntegration = true;
|
||||||
enableZshIntegration = true;
|
enableZshIntegration = true;
|
||||||
installBatSyntax = false; # The file doesn't seem to exist in the pkg
|
installBatSyntax = false; # The file doesn't seem to exist in the pkg
|
||||||
@@ -33,18 +33,21 @@ in
|
|||||||
macos-titlebar-style = "hidden";
|
macos-titlebar-style = "hidden";
|
||||||
window-decoration = false;
|
window-decoration = false;
|
||||||
macos-non-native-fullscreen = true;
|
macos-non-native-fullscreen = true;
|
||||||
quit-after-last-window-closed = lib.mkIf pkgs.stdenv.isDarwin true;
|
quit-after-last-window-closed = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin true;
|
||||||
fullscreen = if pkgs.stdenv.isDarwin then true else false;
|
fullscreen = if pkgs.stdenv.hostPlatform.isDarwin then true else false;
|
||||||
keybind = [
|
keybind = [
|
||||||
"super+t=unbind" # Pass super-t to underlying tool (e.g. zellij tabs)
|
# Translate Mac Super & Ctrl combinations into Alt (ESC prefix) sequences
|
||||||
"super+shift+bracket_right=unbind"
|
# so Zellij receives them without needing Kitty keyboard protocol
|
||||||
"super+shift+bracket_left=unbind"
|
"super+t=text:\\x1bt"
|
||||||
"ctrl+tab=unbind"
|
"super+shift+]=text:\\x1b}"
|
||||||
"ctrl+shift+tab=unbind"
|
"super+shift+[=text:\\x1b{"
|
||||||
"ctrl+tab=text:\\x1b[9;5u"
|
"ctrl+tab=text:\\x1b}"
|
||||||
"ctrl+shift+tab=text:\\x1b[9;6u"
|
"ctrl+shift+tab=text:\\x1b{"
|
||||||
"super+k=unbind"
|
"super+k=text:\\x1bK"
|
||||||
"super+shift+e=unbind"
|
"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" = {
|
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 {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
userName = lib.mkForce cfg.work.name;
|
settings = {
|
||||||
userEmail = lib.mkForce cfg.work.email;
|
user = {
|
||||||
|
name = lib.mkForce cfg.work.name;
|
||||||
|
email = lib.mkForce cfg.work.email;
|
||||||
|
signingKey = "~/.ssh/work_github";
|
||||||
|
};
|
||||||
|
commit = {
|
||||||
|
gpgsign = true;
|
||||||
|
};
|
||||||
|
tag = {
|
||||||
|
gpgsign = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
includes = [
|
includes = [
|
||||||
{
|
{
|
||||||
path = "${config.home.homeDirectory}/${config.xdg.configFile."git/personal".target}";
|
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
|
# Personal git config
|
||||||
xdg.configFile."git/personal".text = lib.generators.toGitINI {
|
xdg.configFile."git/personal".text = lib.generators.toGitINI {
|
||||||
user = {
|
user = {
|
||||||
@@ -57,12 +73,6 @@ in
|
|||||||
email = cfg.personal.email;
|
email = cfg.personal.email;
|
||||||
signingkey = "~/.ssh/id_ed25519";
|
signingkey = "~/.ssh/id_ed25519";
|
||||||
};
|
};
|
||||||
commit = {
|
|
||||||
gpgsign = true;
|
|
||||||
};
|
|
||||||
tag = {
|
|
||||||
gpgsign = true;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Personal jj config
|
# Personal jj config
|
||||||
|
|||||||
@@ -29,9 +29,11 @@ in
|
|||||||
|
|
||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
userName = cfg.name;
|
settings = {
|
||||||
userEmail = cfg.email;
|
user = {
|
||||||
extraConfig = {
|
name = cfg.name;
|
||||||
|
email = cfg.email;
|
||||||
|
};
|
||||||
core.pager = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight | less --no-init";
|
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";
|
interactive.difffilter = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight";
|
||||||
pager = {
|
pager = {
|
||||||
@@ -63,7 +65,7 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
xdg.configFile."git/allowed-signers".text = ''
|
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 = {
|
programs.fish.shellAbbrs = {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ in
|
|||||||
extensions = [
|
extensions = [
|
||||||
pkgs.nmasur.gh-collaborators
|
pkgs.nmasur.gh-collaborators
|
||||||
pkgs.gh-dash
|
pkgs.gh-dash
|
||||||
pkgs.gh-copilot
|
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,62 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.nmasur.presets.programs.helix;
|
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
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -16,13 +72,13 @@ in
|
|||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
# Use Neovim as the editor for git commit messages
|
# 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}";
|
programs.jujutsu.settings.ui.editor = lib.mkForce "${lib.getExe pkgs.helix}";
|
||||||
|
|
||||||
# Set Neovim as the default app for text editing and manual pages
|
# Set Neovim as the default app for text editing and manual pages
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
EDITOR = lib.mkForce "${lib.getExe pkgs.helix}";
|
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;
|
MANWIDTH = 87;
|
||||||
MANROFFOPT = "-c";
|
MANROFFOPT = "-c";
|
||||||
};
|
};
|
||||||
@@ -62,6 +118,11 @@ in
|
|||||||
command = lib.getExe pkgs.marksman;
|
command = lib.getExe pkgs.marksman;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
language-server.rumdl = {
|
||||||
|
command = lib.getExe pkgs.rumdl;
|
||||||
|
args = [ "server" ];
|
||||||
|
};
|
||||||
|
|
||||||
language-server.terraform-ls = {
|
language-server.terraform-ls = {
|
||||||
command = "${lib.getExe pkgs.terraform-ls}";
|
command = "${lib.getExe pkgs.terraform-ls}";
|
||||||
args = [ "serve" ];
|
args = [ "serve" ];
|
||||||
@@ -87,10 +148,16 @@ in
|
|||||||
{
|
{
|
||||||
name = "markdown";
|
name = "markdown";
|
||||||
auto-format = false;
|
auto-format = false;
|
||||||
language-servers = [ "marksman" ];
|
language-servers = [
|
||||||
|
"marksman"
|
||||||
|
"rumdl"
|
||||||
|
];
|
||||||
formatter = {
|
formatter = {
|
||||||
command = lib.getExe pkgs.mdformat;
|
command = lib.getExe pkgs.rumdl;
|
||||||
args = [ "-" ];
|
args = [
|
||||||
|
"fmt"
|
||||||
|
"-"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
# Allows return key to continue the token on the next line
|
# Allows return key to continue the token on the next line
|
||||||
comment-tokens = [
|
comment-tokens = [
|
||||||
@@ -102,9 +169,18 @@ in
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "tfvars";
|
name = "hcl";
|
||||||
|
scope = "source.hcl";
|
||||||
|
# injection-regex = "terraform";
|
||||||
auto-format = true;
|
auto-format = true;
|
||||||
language-servers = [ "terraform-ls" ];
|
language-servers = [ "terraform-ls" ];
|
||||||
|
language-id = "terraform";
|
||||||
|
file-types = [
|
||||||
|
"tf"
|
||||||
|
"tfvars"
|
||||||
|
"terraform"
|
||||||
|
{ glob = "*.auto.tfvars"; }
|
||||||
|
];
|
||||||
formatter = {
|
formatter = {
|
||||||
command = lib.getExe pkgs.terraform;
|
command = lib.getExe pkgs.terraform;
|
||||||
args = [
|
args = [
|
||||||
@@ -114,11 +190,15 @@ in
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name = "hcl";
|
name = "hcl-packer";
|
||||||
|
scope = "source.hcl-packer";
|
||||||
auto-format = true;
|
auto-format = true;
|
||||||
language-servers = [ "terraform-ls" ];
|
file-types = [
|
||||||
|
"hcl"
|
||||||
|
"pkr.hcl"
|
||||||
|
];
|
||||||
formatter = {
|
formatter = {
|
||||||
command = lib.getExe pkgs.terraform;
|
command = "${pkgs.packer}/bin/packer";
|
||||||
args = [
|
args = [
|
||||||
"fmt"
|
"fmt"
|
||||||
"-"
|
"-"
|
||||||
@@ -179,18 +259,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
# Commandline git blame
|
# 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)}";
|
# 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 }'';
|
||||||
# Open yazi
|
space.i = '':open %sh{ ${blame_file_pretty}/bin/blame_file_pretty "%{buffer_name}" %{cursor_line} 3 }'';
|
||||||
# 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"
|
|
||||||
];
|
|
||||||
|
|
||||||
# Extend selection above
|
# Extend selection above
|
||||||
X = "select_line_above";
|
X = "select_line_above";
|
||||||
@@ -256,6 +327,10 @@ in
|
|||||||
git-exclude = true; # Skip excluded files
|
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
|
# Show whitespace visible to the user
|
||||||
# Waiting for trailing whitespace option ideally
|
# Waiting for trailing whitespace option ideally
|
||||||
whitespace = {
|
whitespace = {
|
||||||
@@ -272,7 +347,7 @@ in
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
themes."${config.programs.helix.settings.theme}" = {
|
themes.base16 = {
|
||||||
"attributes" = config.theme.colors.base09;
|
"attributes" = config.theme.colors.base09;
|
||||||
"comment" = {
|
"comment" = {
|
||||||
fg = config.theme.colors.base03;
|
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
|
let
|
||||||
cfg = config.nmasur.presets.programs.jujutsu;
|
cfg = config.nmasur.presets.programs.jujutsu;
|
||||||
|
tomlFormat = pkgs.formats.toml { };
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -20,8 +21,8 @@ in
|
|||||||
# https://github.com/martinvonz/jj/blob/main/docs/config.md
|
# https://github.com/martinvonz/jj/blob/main/docs/config.md
|
||||||
settings = {
|
settings = {
|
||||||
user = {
|
user = {
|
||||||
name = config.programs.git.userName;
|
name = config.programs.git.settings.user.name;
|
||||||
email = config.programs.git.userEmail;
|
email = config.programs.git.settings.user.email;
|
||||||
};
|
};
|
||||||
ui.paginate = "never";
|
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 = [
|
home.packages = [
|
||||||
# Required for the fsmonitor to auto-snapshot
|
# Required for the fsmonitor to auto-snapshot
|
||||||
pkgs.watchman
|
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;
|
nmasur.presets.services.i3.terminal = pkgs.kitty;
|
||||||
|
|
||||||
# Set the Rofi terminal for running programs
|
# 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
|
# Display images in the terminal
|
||||||
programs.fish.interactiveShellInit = # fish
|
programs.fish.interactiveShellInit = # fish
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
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 ];
|
||||||
|
|
||||||
|
# Ctrl-b: EXECUTE the proven manual cure as a real commandline. The cure
|
||||||
|
# is not the variable's end value (it starts and ends at 1) — it is the
|
||||||
|
# reader fully exiting readline and re-entering, which only command
|
||||||
|
# EXECUTION does. Setting the variable inline (a plain binding body, or the
|
||||||
|
# old fish_postexec/fish_cancel hook) never makes the reader exit/re-enter,
|
||||||
|
# so it never cured and, with extra repaints on a wedged reader, made it
|
||||||
|
# worse. `commandline -f execute` reproduces exactly what typing the cure
|
||||||
|
# and pressing Enter does — indistinguishable to fish from the manual cure.
|
||||||
|
# Note: this submits the current commandline, so it runs the cure in place
|
||||||
|
# of whatever is typed (fine for a rescue key hit at an empty prompt).
|
||||||
|
# Ctrl-b chosen because it is otherwise unbound (Ctrl-g is taken).
|
||||||
|
nmasur.presets.programs.fish.fish_user_key_bindings = # fish
|
||||||
|
''
|
||||||
|
for mode in insert default visual
|
||||||
|
bind -M $mode \cb heal-autosuggest
|
||||||
|
end
|
||||||
|
'';
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
lag-sample = {
|
||||||
|
description = "Stack-sample fish and zellij while typing lag is happening";
|
||||||
|
body = builtins.readFile ./lag-sample.fish;
|
||||||
|
};
|
||||||
|
heal-autosuggest = {
|
||||||
|
description = "Heal post-TUI typing lag by executing the autosuggestion-toggle cure (bind to a key)";
|
||||||
|
# Replace the commandline with the exact cure the user runs by hand and
|
||||||
|
# execute it. Executing (not inline-setting) is what cures: it forces
|
||||||
|
# the reader to leave and re-enter readline. Runs in place of whatever
|
||||||
|
# is currently typed.
|
||||||
|
body = # fish
|
||||||
|
''
|
||||||
|
commandline -r 'set -g fish_autosuggestion_enabled 0; and set -g fish_autosuggestion_enabled 1'
|
||||||
|
commandline -f execute
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Capture stack samples of this fish process, the zellij server, and the
|
||||||
|
# zellij client WHILE the typing lag is happening. This names the guilty
|
||||||
|
# component directly: if fish's main thread is busy/blocked per keystroke the
|
||||||
|
# stacks show exactly where; if fish is idle while typing feels laggy, the
|
||||||
|
# delay is in zellij's render path instead.
|
||||||
|
#
|
||||||
|
# CAUTION (learned 2026-08-30): attaching the sampler to a lagging fish CURES
|
||||||
|
# the lag (thread suspend/resume unwedges it), so run this from a DIFFERENT
|
||||||
|
# pane with the lagging shell's pid: `lag-sample <pid>` (get it in the lagging
|
||||||
|
# shell with the builtin-only `echo $fish_pid`). Have someone type in the
|
||||||
|
# lagging pane while sampling runs — the first samples may catch the wedge.
|
||||||
|
# With no argument it samples the current shell.
|
||||||
|
|
||||||
|
set -l target $fish_pid
|
||||||
|
if test (count $argv) -ge 1; and test -n "$argv[1]"
|
||||||
|
set target $argv[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
set -l outdir ~/.local/state/lag-triage
|
||||||
|
mkdir -p $outdir
|
||||||
|
set -l ts (date +%Y%m%d-%H%M%S)
|
||||||
|
set -l dur 8
|
||||||
|
|
||||||
|
set -l fishfile $outdir/sample-$ts-fish-$target.txt
|
||||||
|
/usr/bin/sample $target $dur 1 -file $fishfile &>/dev/null &
|
||||||
|
disown
|
||||||
|
|
||||||
|
# this session's zellij server (socket path ends in the session name)
|
||||||
|
set -l serverpid (pgrep -f "zellij --server.*/$ZELLIJ_SESSION_NAME\$")
|
||||||
|
test -z "$serverpid"; and set serverpid (pgrep -f "zellij --server" | head -3)
|
||||||
|
for pid in $serverpid
|
||||||
|
/usr/bin/sample $pid $dur 1 -file $outdir/sample-$ts-zellij-server-$pid.txt &>/dev/null &
|
||||||
|
disown
|
||||||
|
end
|
||||||
|
|
||||||
|
# zellij clients (attached to ghostty): named zellij but without --server args
|
||||||
|
set -l allserver (pgrep -f "zellij --server")
|
||||||
|
set -l clientpid
|
||||||
|
for pid in (pgrep -x zellij)
|
||||||
|
contains $pid $allserver; or set -a clientpid $pid
|
||||||
|
end
|
||||||
|
for pid in $clientpid[1..3]
|
||||||
|
/usr/bin/sample $pid $dur 1 -file $outdir/sample-$ts-zellij-client-$pid.txt &>/dev/null &
|
||||||
|
disown
|
||||||
|
end
|
||||||
|
|
||||||
|
# notify when done, without occupying the commandline
|
||||||
|
fish -c "sleep (math $dur + 2); echo; echo '== lag-sample done: '$outdir'/sample-$ts-*.txt =='" &
|
||||||
|
disown
|
||||||
|
|
||||||
|
echo "Sampling fish (pid $target), zellij server(s) [$serverpid], client(s) [$clientpid] for $dur s."
|
||||||
|
echo ">>> TYPE CONTINUOUSLY IN THE LAGGING PANE NOW (junk text is fine) <<<"
|
||||||
|
echo "Files: $outdir/sample-$ts-*.txt"
|
||||||
@@ -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 \
|
-E pr=5000/prompt \
|
||||||
-H "${ldap_scheme}://''${LDAP_HOST}:${builtins.toString ldap_port}" \
|
-H "${ldap_scheme}://''${LDAP_HOST}:${builtins.toString ldap_port}" \
|
||||||
-D "${pkgs.lib.toUpper magic_prefix}2\\${pkgs.lib.toLower config.home.username}" \
|
-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=}" \
|
-b "dc=''${LDAP_HOST//./,dc=}" \
|
||||||
-s "sub" -x "(cn=''${SEARCH_FILTER})" \
|
-s "sub" -x "(cn=''${SEARCH_FILTER})" \
|
||||||
| ${jq_parse}/bin/ljq
|
| ${jq_parse}/bin/ljq
|
||||||
|
|||||||
@@ -38,13 +38,13 @@ in
|
|||||||
);
|
);
|
||||||
|
|
||||||
# Use Neovim as the editor for git commit messages
|
# 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}";
|
programs.jujutsu.settings.ui.editor = "${lib.getExe cfg.package}";
|
||||||
|
|
||||||
# Set Neovim as the default app for text editing and manual pages
|
# Set Neovim as the default app for text editing and manual pages
|
||||||
home.sessionVariables = {
|
home.sessionVariables = {
|
||||||
EDITOR = "${lib.getExe cfg.package}";
|
EDITOR = "${lib.getExe cfg.package}";
|
||||||
MANPAGER = "${lib.getExe cfg.package} +Man!";
|
# MANPAGER = "${lib.getExe cfg.package} +Man!";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Create quick aliases for launching Neovim
|
# Create quick aliases for launching Neovim
|
||||||
@@ -63,10 +63,10 @@ in
|
|||||||
# Create a desktop option for launching Neovim from a file manager
|
# Create a desktop option for launching Neovim from a file manager
|
||||||
# (Requires launching the terminal and then executing Neovim)
|
# (Requires launching the terminal and then executing Neovim)
|
||||||
xdg.desktopEntries.nvim =
|
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";
|
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 = [
|
mimeType = [
|
||||||
"text/plain"
|
"text/plain"
|
||||||
"text/markdown"
|
"text/markdown"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
pkgs,
|
# pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
@@ -23,17 +23,18 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Create nix-index if doesn't exist
|
# Create nix-index if doesn't exist
|
||||||
home.activation.createNixIndex =
|
# Should not be necessary with the nix-index-database flake
|
||||||
let
|
# home.activation.createNixIndex =
|
||||||
cacheDir = "${config.xdg.cacheHome}/nix-index";
|
# let
|
||||||
in
|
# cacheDir = "${config.xdg.cacheHome}/nix-index";
|
||||||
lib.mkIf config.programs.nix-index.enable (
|
# in
|
||||||
config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
# lib.mkIf config.programs.nix-index.enable (
|
||||||
if [ ! -d ${cacheDir} ]; then
|
# config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
run ${pkgs.nix-index}/bin/nix-index -f ${pkgs.path}
|
# if [ ! -d ${cacheDir} ]; then
|
||||||
fi
|
# run ${pkgs.nix-index}/bin/nix-index -f ${pkgs.path}
|
||||||
''
|
# fi
|
||||||
);
|
# ''
|
||||||
|
# );
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ in
|
|||||||
description = "Nixpkgs tools for macOS";
|
description = "Nixpkgs tools for macOS";
|
||||||
default =
|
default =
|
||||||
config.nmasur.presets.programs.nixpkgs.enable
|
config.nmasur.presets.programs.nixpkgs.enable
|
||||||
&& pkgs.stdenv.isDarwin
|
&& pkgs.stdenv.hostPlatform.isDarwin
|
||||||
&& config.nmasur.presets.programs.dotfiles.enable;
|
&& config.nmasur.presets.programs.dotfiles.enable;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,18 @@ in
|
|||||||
doas nixos-rebuild switch --flake ${config.nmasur.presets.programs.dotfiles.path}
|
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";
|
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 ];
|
home.packages = [ pkgs.noti ];
|
||||||
programs.fish.shellAbbrs = {
|
programs.fish = {
|
||||||
# Add noti for ghpr in Darwin
|
shellAbbrs = {
|
||||||
ghpr = lib.mkForce "gh pr create && sleep 3 && noti gh run watch";
|
# Add noti for ghpr in Darwin
|
||||||
grw = lib.mkForce "noti gh run watch";
|
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
|
# Broken on 2023-12-11
|
||||||
# https://forum.obsidian.md/t/electron-25-is-now-eol-please-upgrade-to-a-newer-version/72878/8
|
# 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/
|
.terraform/
|
||||||
.target/
|
.target/
|
||||||
/Library/
|
/Library/
|
||||||
|
.jj/
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
|
home.packages = [ pkgs.jj-starship ];
|
||||||
|
|
||||||
programs.starship = {
|
programs.starship = {
|
||||||
enable = true;
|
enable = true;
|
||||||
enableFishIntegration = true;
|
enableFishIntegration = true;
|
||||||
@@ -27,6 +29,7 @@ in
|
|||||||
"$git_branch"
|
"$git_branch"
|
||||||
"$git_commit"
|
"$git_commit"
|
||||||
"$git_status"
|
"$git_status"
|
||||||
|
"\${custom.jj}"
|
||||||
"$hostname"
|
"$hostname"
|
||||||
"$cmd_duration"
|
"$cmd_duration"
|
||||||
"$character"
|
"$character"
|
||||||
@@ -38,23 +41,37 @@ in
|
|||||||
vicmd_symbol = "[❮](bold green)";
|
vicmd_symbol = "[❮](bold green)";
|
||||||
};
|
};
|
||||||
cmd_duration = {
|
cmd_duration = {
|
||||||
min_time = 5000;
|
min_time = 5001;
|
||||||
show_notifications = if pkgs.stdenv.isLinux then false else true;
|
show_notifications = if pkgs.stdenv.hostPlatform.isLinux then false else true;
|
||||||
min_time_to_notify = 30000;
|
min_time_to_notify = 30000;
|
||||||
format = "[$duration]($style) ";
|
format = "[$duration]($style) ";
|
||||||
};
|
};
|
||||||
|
custom = {
|
||||||
|
jj = {
|
||||||
|
when = "jj-starship detect";
|
||||||
|
shell = [
|
||||||
|
"jj-starship"
|
||||||
|
"--no-jj-prefix"
|
||||||
|
"--no-git-prefix"
|
||||||
|
];
|
||||||
|
format = "$output ";
|
||||||
|
};
|
||||||
|
};
|
||||||
directory = {
|
directory = {
|
||||||
truncate_to_repo = true;
|
truncate_to_repo = true;
|
||||||
truncation_length = 100;
|
truncation_length = 100;
|
||||||
};
|
};
|
||||||
git_branch = {
|
git_branch = {
|
||||||
|
disabled = true;
|
||||||
format = "[$symbol$branch]($style)";
|
format = "[$symbol$branch]($style)";
|
||||||
};
|
};
|
||||||
git_commit = {
|
git_commit = {
|
||||||
|
disabled = true;
|
||||||
format = "( @ [$hash]($style) )";
|
format = "( @ [$hash]($style) )";
|
||||||
only_detached = false;
|
only_detached = false;
|
||||||
};
|
};
|
||||||
git_status = {
|
git_status = {
|
||||||
|
disabled = true;
|
||||||
format = "([$all_status$ahead_behind]($style) )";
|
format = "([$all_status$ahead_behind]($style) )";
|
||||||
conflicted = "=";
|
conflicted = "=";
|
||||||
ahead = "⇡";
|
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 = 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
|
-- Fix color blocks instead of text
|
||||||
config.front_end = "WebGpu"
|
config.front_end = "WebGpu"
|
||||||
@@ -114,7 +114,7 @@ in
|
|||||||
config.hide_tab_bar_if_only_one_tab = true
|
config.hide_tab_bar_if_only_one_tab = true
|
||||||
config.window_frame = {
|
config.window_frame = {
|
||||||
font = wezterm.font('${cfg.font}', { weight = 'Bold'}),
|
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 = {
|
config.colors = {
|
||||||
@@ -191,7 +191,7 @@ in
|
|||||||
-- super-t open new tab in new dir
|
-- super-t open new tab in new dir
|
||||||
{
|
{
|
||||||
key = 't',
|
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 {
|
action = wezterm.action.SpawnCommandInNewTab {
|
||||||
cwd = wezterm.home_dir,
|
cwd = wezterm.home_dir,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ in
|
|||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
programs.zed-editor = {
|
programs.zed-editor = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
package = pkgs.stable.zed-editor;
|
||||||
|
|
||||||
extensions = [
|
extensions = [
|
||||||
"nix"
|
"nix"
|
||||||
|
|||||||
@@ -6,15 +6,45 @@
|
|||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
inherit (config.nmasur.settings) username;
|
||||||
cfg = config.nmasur.presets.programs.zellij;
|
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.
|
||||||
|
# Flight recorder for the still-unsolved post-TUI typing lag: sampling the
|
||||||
|
# process CURES the lag (a wedged thread gets kicked loose), so the only way
|
||||||
|
# to observe it is a recorder that is already running before the lag starts.
|
||||||
|
# Armed by `touch ~/.local/state/lag-triage/RECORD`; new panes then log
|
||||||
|
# fish's reader/thread internals to ~/.local/state/lag-triage/flight/.
|
||||||
|
# Remove the RECORD file to disable (zero overhead when off).
|
||||||
|
fish-no-query-term = pkgs.writeShellScriptBin "fish-no-query-term" ''
|
||||||
|
export fish_features=no-query-term
|
||||||
|
dir="$HOME/.local/state/lag-triage"
|
||||||
|
if [ -e "$dir/RECORD" ]; then
|
||||||
|
mkdir -p "$dir/flight"
|
||||||
|
find "$dir/flight" -type f -mtime +3 -delete 2>/dev/null
|
||||||
|
export FISH_DEBUG='reader,term-support,proc-termowner,iothread,fd-monitor,topic-monitor'
|
||||||
|
export FISH_DEBUG_OUTPUT="$dir/flight/fish-$(date +%Y%m%d-%H%M%S)-$$.log"
|
||||||
|
fi
|
||||||
|
exec ${lib.getExe pkgs.fish} "$@"
|
||||||
|
'';
|
||||||
|
|
||||||
zellij-switch-to-last = pkgs.writeShellScriptBin "zellij-switch-to-last" ''
|
zellij-switch-to-last = pkgs.writeShellScriptBin "zellij-switch-to-last" ''
|
||||||
TARGET_SESSION=$(cat ~/.local/state/zellij-last-session)
|
TARGET_SESSION=$(cat ~/.local/state/zellij-last-session)
|
||||||
if [ -z "$TARGET_SESSION" ]; then
|
if [ -z "$TARGET_SESSION" ]; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
echo "$ZELLIJ_SESSION_NAME" > ~/.local/state/zellij-last-session
|
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
|
in
|
||||||
|
|
||||||
@@ -24,8 +54,6 @@ in
|
|||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
|
||||||
home.packages = [ pkgs.zellij-switch ];
|
|
||||||
|
|
||||||
programs.fish = {
|
programs.fish = {
|
||||||
shellAbbrs.z = "zellij";
|
shellAbbrs.z = "zellij";
|
||||||
functions = {
|
functions = {
|
||||||
@@ -33,26 +61,63 @@ in
|
|||||||
# description = "Open a session in Zellij";
|
# description = "Open a session in Zellij";
|
||||||
body = # fish
|
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
|
if test -z $TARGET_DIR
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
if test "$TARGET_DIR" = $(pwd)
|
if test "$TARGET_DIR" = $(pwd)
|
||||||
return 1
|
return 1
|
||||||
end
|
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
|
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 = {
|
gh-run = {
|
||||||
body = # fish
|
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
|
xdg.configFile."zellij/layouts/compact-top.kdl".text = # kdl
|
||||||
''
|
''
|
||||||
layout {
|
layout {
|
||||||
@@ -85,19 +150,55 @@ in
|
|||||||
enableFishIntegration = true;
|
enableFishIntegration = true;
|
||||||
enableZshIntegration = true;
|
enableZshIntegration = true;
|
||||||
attachExistingSession = true;
|
attachExistingSession = true;
|
||||||
exitShellOnExit = false;
|
exitShellOnExit = true;
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
default_mode = "locked";
|
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";
|
# default_layout = "compact-top";
|
||||||
# Remove border
|
# Remove border
|
||||||
pane_frames = false;
|
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
|
||||||
scrollback_editor = config.home.sessionVariables.EDITOR;
|
scrollback_editor = config.home.sessionVariables.EDITOR;
|
||||||
|
|
||||||
show_startup_tips = false;
|
show_startup_tips = false;
|
||||||
|
|
||||||
keybinds = {
|
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 = {
|
session = {
|
||||||
"bind \"w\"" = {
|
"bind \"w\"" = {
|
||||||
LaunchOrFocusPlugin = {
|
LaunchOrFocusPlugin = {
|
||||||
@@ -121,9 +222,15 @@ in
|
|||||||
shared = {
|
shared = {
|
||||||
"bind \"Alt Shift s\"" = {
|
"bind \"Alt Shift s\"" = {
|
||||||
Run = {
|
Run = {
|
||||||
_args = [
|
_args =
|
||||||
(lib.getExe zellij-switch-to-last)
|
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;
|
close_on_exit = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -155,11 +262,13 @@ in
|
|||||||
};
|
};
|
||||||
"bind \"Alt Shift w\"" = {
|
"bind \"Alt Shift w\"" = {
|
||||||
Run = {
|
Run = {
|
||||||
_args = [
|
_args =
|
||||||
(lib.getExe pkgs.gh)
|
(if config.nmasur.presets.programs.noti.enable then [ (lib.getExe pkgs.noti) ] else [ ])
|
||||||
"run"
|
++ [
|
||||||
"watch"
|
(lib.getExe pkgs.gh)
|
||||||
];
|
"run"
|
||||||
|
"watch"
|
||||||
|
];
|
||||||
# direction = "Right";
|
# direction = "Right";
|
||||||
# close_on_exit = false;
|
# close_on_exit = false;
|
||||||
# start_suspended = true;
|
# start_suspended = true;
|
||||||
@@ -186,9 +295,28 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
"bind \"Alt Shift j\"" = {
|
"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 = {
|
Run = {
|
||||||
_args = [
|
_args = [
|
||||||
(lib.getExe pkgs.lazyjj)
|
(lib.getExe pkgs.yazi)
|
||||||
];
|
];
|
||||||
close_on_exit = true;
|
close_on_exit = true;
|
||||||
floating = true;
|
floating = true;
|
||||||
@@ -201,33 +329,62 @@ in
|
|||||||
"bind \"Super Shift ]\"" = {
|
"bind \"Super Shift ]\"" = {
|
||||||
GoToNextTab = { };
|
GoToNextTab = { };
|
||||||
};
|
};
|
||||||
|
"bind \"Alt ]\"" = {
|
||||||
|
GoToNextTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt }\"" = {
|
||||||
|
GoToNextTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt Shift ]\"" = {
|
||||||
|
GoToNextTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt Shift }\"" = {
|
||||||
|
GoToNextTab = { };
|
||||||
|
};
|
||||||
"bind \"Super Shift [\"" = {
|
"bind \"Super Shift [\"" = {
|
||||||
GoToPreviousTab = { };
|
GoToPreviousTab = { };
|
||||||
};
|
};
|
||||||
|
"bind \"Alt [\"" = {
|
||||||
|
GoToPreviousTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt {\"" = {
|
||||||
|
GoToPreviousTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt Shift [\"" = {
|
||||||
|
GoToPreviousTab = { };
|
||||||
|
};
|
||||||
|
"bind \"Alt Shift {\"" = {
|
||||||
|
GoToPreviousTab = { };
|
||||||
|
};
|
||||||
"bind \"Ctrl Tab\"" = {
|
"bind \"Ctrl Tab\"" = {
|
||||||
GoToNextTab = { };
|
GoToNextTab = { };
|
||||||
};
|
};
|
||||||
"bind \"Ctrl Shift Tab\"" = {
|
"bind \"Ctrl Shift Tab\"" = {
|
||||||
GoToPreviousTab = { };
|
GoToPreviousTab = { };
|
||||||
};
|
};
|
||||||
"bind \"Super t\"" = lib.mkIf pkgs.stdenv.isDarwin {
|
"bind \"Super t\"" = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||||
NewTab = { };
|
NewTab = { };
|
||||||
};
|
};
|
||||||
"bind \"Alt t\"" = lib.mkIf pkgs.stdenv.isLinux {
|
"bind \"Alt t\"" = {
|
||||||
NewTab = { };
|
NewTab = { };
|
||||||
};
|
};
|
||||||
"bind \"Super k\"" = lib.mkIf pkgs.stdenv.isDarwin {
|
"bind \"Super k\"" = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin {
|
||||||
SwitchToMode = {
|
SwitchToMode = {
|
||||||
_args = [ "scroll" ];
|
_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 = { };
|
EditScrollback = { };
|
||||||
SwitchToMode = {
|
SwitchToMode = {
|
||||||
_args = [ "locked" ];
|
_args = [ "locked" ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
"bind \"Alt Shift e\"" = lib.mkIf pkgs.stdenv.isLinux {
|
"bind \"Alt Shift e\"" = {
|
||||||
EditScrollback = { };
|
EditScrollback = { };
|
||||||
SwitchToMode = {
|
SwitchToMode = {
|
||||||
_args = [ "locked" ];
|
_args = [ "locked" ];
|
||||||
@@ -243,6 +400,16 @@ in
|
|||||||
_args = [ "Left" ];
|
_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("ctrl", "space", function() end)
|
||||||
self.launcher:bind("", "return", function()
|
self.launcher:bind("", "return", function()
|
||||||
-- self:switch("@wezterm@")
|
-- self:switch("@wezterm@")
|
||||||
self:switch("/Applications/Ghostty.app")
|
self:switch("@ghostty@")
|
||||||
end)
|
end)
|
||||||
self.launcher:bind("", "C", function()
|
self.launcher:bind("", "C", function()
|
||||||
self:switch("Calendar.app")
|
self:switch("Calendar.app")
|
||||||
@@ -80,6 +80,9 @@ function obj:init()
|
|||||||
self.launcher:bind("", "H", function()
|
self.launcher:bind("", "H", function()
|
||||||
self:switch("Hammerspoon.app")
|
self:switch("Hammerspoon.app")
|
||||||
end)
|
end)
|
||||||
|
self.launcher:bind("", "L", function()
|
||||||
|
self:switch("Claude.app")
|
||||||
|
end)
|
||||||
self.launcher:bind("", "M", function()
|
self.launcher:bind("", "M", function()
|
||||||
self:switch("Messages.app")
|
self:switch("Messages.app")
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.nmasur.presets.services.hammerspoon;
|
cfg = config.nmasur.presets.services.hammerspoon;
|
||||||
|
# inherit (config.nmasur.settings) username;
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -15,25 +16,47 @@ in
|
|||||||
lib.mkEnableOption "Hammerspoon macOS automation";
|
lib.mkEnableOption "Hammerspoon macOS automation";
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
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/init.lua".source = ./init.lua;
|
||||||
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./Spoons/ControlEscape.spoon;
|
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./Spoons/ControlEscape.spoon;
|
||||||
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./Spoons/DismissAlerts.spoon;
|
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./Spoons/DismissAlerts.spoon;
|
||||||
xdg.configFile."hammerspoon/Spoons/Launcher.spoon/init.lua".source =
|
xdg.configFile."hammerspoon/Spoons/Launcher.spoon/init.lua".source =
|
||||||
pkgs.replaceVars ./Spoons/Launcher.spoon/init.lua
|
pkgs.replaceVars ./Spoons/Launcher.spoon/init.lua
|
||||||
{
|
{
|
||||||
discord = "${pkgs.discord}/Applications/Discord.app";
|
discord = "Discord.app";
|
||||||
firefox = "${pkgs.firefox-unwrapped}/Applications/Firefox.app";
|
firefox = "${pkgs.firefox-unwrapped}/Applications/Firefox.app";
|
||||||
|
ghostty = "${pkgs.ghostty-bin}/Applications/Ghostty.app";
|
||||||
obsidian = "${pkgs.obsidian}/Applications/Obsidian.app";
|
obsidian = "${pkgs.obsidian}/Applications/Obsidian.app";
|
||||||
slack = "${pkgs.slack}/Applications/Slack.app";
|
slack = "${pkgs.slack}/Applications/Slack.app";
|
||||||
wezterm = "${pkgs.wezterm}/Applications/WezTerm.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/MoveWindow.spoon".source = ./Spoons/MoveWindow.spoon;
|
||||||
|
xdg.configFile."hammerspoon/Spoons/HideZoomWindow.spoon".source = ./Spoons/HideZoomWindow.spoon;
|
||||||
|
|
||||||
home.activation.reloadHammerspoon = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
home.activation.reloadHammerspoon = config.lib.dag.entryAfter [ "writeBoundary" ] ''
|
||||||
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.reload()"
|
# The `hs` CLI blocks forever on Hammerspoon's IPC message port when the
|
||||||
$DRY_RUN_CMD sleep 1
|
# app isn't running (|| true can't help — it never returns). Only reload
|
||||||
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.console.clearConsole()"
|
# 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("Launcher"):init()
|
||||||
hs.loadSpoon("DismissAlerts"):init()
|
hs.loadSpoon("DismissAlerts"):init()
|
||||||
hs.loadSpoon("MoveWindow"):init()
|
hs.loadSpoon("MoveWindow"):init()
|
||||||
|
hs.loadSpoon("HideZoomWindow"):init()
|
||||||
|
|||||||
@@ -214,9 +214,9 @@ in
|
|||||||
cfg.commands.lockScreen != null
|
cfg.commands.lockScreen != null
|
||||||
) "exec ${cfg.commands.lockScreen}";
|
) "exec ${cfg.commands.lockScreen}";
|
||||||
"${modifier}+Mod1+h" =
|
"${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" =
|
"${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
|
# Window options
|
||||||
"${modifier}+q" = "kill";
|
"${modifier}+q" = "kill";
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Automatically check for mail and keep files synced locally
|
# 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;
|
enable = true;
|
||||||
frequency = "*:0/5";
|
frequency = "*:0/5";
|
||||||
postExec = "${lib.getExe pkgs.notmuch} new";
|
postExec = "${lib.getExe pkgs.notmuch} new";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Used to watch for new mail and trigger sync
|
# 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
|
# Allows sending email from CLI/sendmail
|
||||||
programs.msmtp.enable = true;
|
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.username = config.nmasur.settings.username;
|
||||||
home.homeDirectory =
|
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 = [
|
home.packages = [
|
||||||
pkgs.dig # DNS lookup
|
pkgs.dig # DNS lookup
|
||||||
@@ -33,6 +33,7 @@ in
|
|||||||
nmasur.presets = {
|
nmasur.presets = {
|
||||||
programs = {
|
programs = {
|
||||||
bat.enable = lib.mkDefault true;
|
bat.enable = lib.mkDefault true;
|
||||||
|
chawan.enable = lib.mkDefault true;
|
||||||
fd.enable = lib.mkDefault true;
|
fd.enable = lib.mkDefault true;
|
||||||
ripgrep.enable = lib.mkDefault true;
|
ripgrep.enable = lib.mkDefault true;
|
||||||
starship.enable = lib.mkDefault true;
|
starship.enable = lib.mkDefault true;
|
||||||
|
|||||||
@@ -17,33 +17,24 @@ in
|
|||||||
|
|
||||||
nmasur.presets = {
|
nmasur.presets = {
|
||||||
fonts.enable = lib.mkDefault true;
|
fonts.enable = lib.mkDefault true;
|
||||||
services.hammerspoon.enable = lib.mkDefault true;
|
services = {
|
||||||
programs.nixpkgs-darwin.enable = lib.mkDefault true;
|
darwin-settings.enable = lib.mkDefault true;
|
||||||
programs.mpv.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}";
|
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"'
|
# 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
|
pkgs.pgcli # Postgres client with autocomplete
|
||||||
];
|
];
|
||||||
|
|
||||||
|
nmasur.presets = {
|
||||||
|
programs.cargo.enable = lib.mkDefault true;
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,31 @@ in
|
|||||||
|
|
||||||
nmasur.presets.programs = {
|
nmasur.presets.programs = {
|
||||||
zed-editor.enable = lib.mkDefault true;
|
zed-editor.enable = lib.mkDefault true;
|
||||||
jujutsu.enable = lib.mkDefault true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
home.packages = [
|
home.packages = [
|
||||||
|
|
||||||
|
# pkgs.nmasur.ren-find # Rename files
|
||||||
|
# pkgs.nmasur.rep-grep # Replace text in files
|
||||||
|
|
||||||
# Charm tools
|
# Charm tools
|
||||||
|
|
||||||
pkgs.glow # Markdown previews
|
pkgs.glow # Markdown previews
|
||||||
pkgs.skate # Key-value store
|
pkgs.skate # Key-value store
|
||||||
pkgs.charm # Manage account and filesystem
|
pkgs.charm # Manage account and filesystem
|
||||||
pkgs.pop # Send emails from a TUI
|
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";
|
publicShare = lib.mkDefault "$HOME/other/public";
|
||||||
templates = lib.mkDefault "$HOME/other/templates";
|
templates = lib.mkDefault "$HOME/other/templates";
|
||||||
extraConfig = {
|
extraConfig = {
|
||||||
XDG_DEV_DIR = lib.mkDefault "$HOME/dev";
|
DEV = lib.mkDefault "$HOME/dev";
|
||||||
};
|
};
|
||||||
|
setSessionVariables = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
nmasur.presets.programs = {
|
nmasur.presets.programs = {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ in
|
|||||||
nsxiv.enable = lib.mkDefault true;
|
nsxiv.enable = lib.mkDefault true;
|
||||||
obsidian.enable = lib.mkDefault true;
|
obsidian.enable = lib.mkDefault true;
|
||||||
rofi.enable = lib.mkDefault true;
|
rofi.enable = lib.mkDefault true;
|
||||||
|
thunderbird.enable = lib.mkDefault true;
|
||||||
xclip.enable = lib.mkDefault true;
|
xclip.enable = lib.mkDefault true;
|
||||||
zathura.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.age # Encryption
|
||||||
pkgs.bc # Calculator
|
pkgs.bc # Calculator
|
||||||
pkgs.bottom # System monitor (top)
|
pkgs.bottom # System monitor (top)
|
||||||
|
pkgs.csvlens # CSV viewer (easier than visidata)
|
||||||
pkgs.delta # Fancy diffs
|
pkgs.delta # Fancy diffs
|
||||||
pkgs.difftastic # Other fancy diffs
|
pkgs.difftastic # Other fancy diffs
|
||||||
pkgs.doggo # DNS client (dig)
|
pkgs.doggo # DNS client (dig)
|
||||||
pkgs.du-dust # Disk usage tree (ncdu)
|
pkgs.dust # Disk usage tree (ncdu)
|
||||||
pkgs.dua # File sizes (du)
|
pkgs.dua # File sizes (du)
|
||||||
pkgs.duf # Basic disk information (df)
|
pkgs.duf # Basic disk information (df)
|
||||||
pkgs.jless # JSON viewer
|
pkgs.jless # JSON viewer
|
||||||
pkgs.jo # JSON output
|
pkgs.jo # JSON output
|
||||||
pkgs.mpd # TUI slideshows
|
pkgs.stable.mpd # TUI slideshows
|
||||||
pkgs.nixfmt-rfc-style # Format Nix code
|
pkgs.nixfmt # Format Nix code
|
||||||
pkgs.nmasur.jqr # FZF fq JSON tool
|
pkgs.nmasur.jqr # FZF fq JSON tool
|
||||||
pkgs.nmasur.osc # Clipboard over SSH
|
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.pandoc # Convert text documents
|
||||||
pkgs.qrencode # Generate qr codes
|
pkgs.qrencode # Generate qr codes
|
||||||
pkgs.spacer # Output lines in terminal
|
pkgs.spacer # Output lines in terminal
|
||||||
pkgs.tealdeer # Cheatsheets
|
pkgs.stable.tealdeer # Cheatsheets
|
||||||
pkgs.tree # Print tree in terminal
|
pkgs.tree # Print tree in terminal
|
||||||
pkgs.vimv-rs # Batch rename files
|
pkgs.vimv-rs # Batch rename files
|
||||||
pkgs.yazi # TUI file explorer
|
pkgs.yazi # TUI file explorer
|
||||||
@@ -42,7 +41,7 @@ in
|
|||||||
|
|
||||||
programs.fish.shellAliases = {
|
programs.fish.shellAliases = {
|
||||||
"du" = lib.mkDefault (lib.getExe pkgs.dua);
|
"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);
|
"df" = lib.mkDefault (lib.getExe pkgs.duf);
|
||||||
|
|
||||||
# Use eza (exa) instead of ls for fancier output
|
# Use eza (exa) instead of ls for fancier output
|
||||||
@@ -63,8 +62,10 @@ in
|
|||||||
ghostty.enable = lib.mkDefault true;
|
ghostty.enable = lib.mkDefault true;
|
||||||
git.enable = lib.mkDefault true;
|
git.enable = lib.mkDefault true;
|
||||||
helix.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;
|
lazygit.enable = lib.mkDefault true;
|
||||||
neovim.enable = lib.mkDefault true;
|
# neovim.enable = lib.mkDefault true;
|
||||||
nix-index.enable = lib.mkDefault true;
|
nix-index.enable = lib.mkDefault true;
|
||||||
nixpkgs.enable = lib.mkDefault true;
|
nixpkgs.enable = lib.mkDefault true;
|
||||||
notes.enable = lib.mkDefault true;
|
notes.enable = lib.mkDefault true;
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
home.packages = [
|
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.dos2unix # Convert Windows text files
|
||||||
pkgs.inetutils # Includes telnet
|
pkgs.inetutils # Includes telnet
|
||||||
pkgs.gnupg # Encryption
|
pkgs.gnupg # Encryption
|
||||||
@@ -29,6 +31,7 @@ in
|
|||||||
pkgs.ssm-session-manager-plugin
|
pkgs.ssm-session-manager-plugin
|
||||||
pkgs.awslogs
|
pkgs.awslogs
|
||||||
pkgs.stu # TUI for AWS S3
|
pkgs.stu # TUI for AWS S3
|
||||||
|
pkgs.pre-commit
|
||||||
pkgs.google-cloud-sdk
|
pkgs.google-cloud-sdk
|
||||||
pkgs.vault-bin
|
pkgs.vault-bin
|
||||||
pkgs.ipcalc # Make IP network calculations
|
pkgs.ipcalc # Make IP network calculations
|
||||||
@@ -37,18 +40,28 @@ in
|
|||||||
pkgs.nmasur.ocr # Converts images to text
|
pkgs.nmasur.ocr # Converts images to text
|
||||||
pkgs.nmasur.aws-ec2 # Browse EC2 instances
|
pkgs.nmasur.aws-ec2 # Browse EC2 instances
|
||||||
pkgs.nmasur.terraform-init # Quick shortcut for initializing Terraform backend
|
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";
|
programs.fish.shellAliases.ec2 = "aws-ec2";
|
||||||
|
|
||||||
nmasur.presets = {
|
nmasur.presets = {
|
||||||
fonts.enable = lib.mkDefault true;
|
fonts.enable = lib.mkDefault true;
|
||||||
programs = {
|
programs = {
|
||||||
_1password.enable = lib.mkDefault true;
|
# _1password.enable = lib.mkDefault true;
|
||||||
atuin.enable = lib.mkDefault true;
|
atuin.enable = lib.mkDefault true;
|
||||||
aws-ssh.enable = lib.mkDefault true;
|
aws-ssh.enable = lib.mkDefault true;
|
||||||
bash.enable = lib.mkDefault true;
|
bash.enable = lib.mkDefault true;
|
||||||
bat.enable = lib.mkDefault true;
|
bat.enable = lib.mkDefault true;
|
||||||
|
cargo.enable = lib.mkDefault true;
|
||||||
direnv.enable = lib.mkDefault true;
|
direnv.enable = lib.mkDefault true;
|
||||||
dotfiles.enable = lib.mkDefault true;
|
dotfiles.enable = lib.mkDefault true;
|
||||||
fd.enable = lib.mkDefault true;
|
fd.enable = lib.mkDefault true;
|
||||||
@@ -67,8 +80,12 @@ in
|
|||||||
ripgrep.enable = lib.mkDefault true;
|
ripgrep.enable = lib.mkDefault true;
|
||||||
starship.enable = lib.mkDefault true;
|
starship.enable = lib.mkDefault true;
|
||||||
terraform.enable = lib.mkDefault true;
|
terraform.enable = lib.mkDefault true;
|
||||||
|
thunderbird.enable = lib.mkDefault true;
|
||||||
weather.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
|
# Speeds up fish launch time on macOS
|
||||||
programs.fish.useBabelfish = true;
|
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 = {
|
global = {
|
||||||
brewfile = true; # Run brew bundle from anywhere
|
brewfile = true; # Run brew bundle from anywhere
|
||||||
lockfiles = false; # Don't save lockfile (since running from anywhere)
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user