Merge branch 'platform'

This is a large refactor to the dotfiles setup.
This commit is contained in:
Noah Masur
2025-03-27 13:33:58 -04:00
452 changed files with 10950 additions and 9708 deletions

View File

@ -1,9 +0,0 @@
# Modules
| Module | Purpose |
| --- | --- |
| [common](./common/default.nix) | User programs and OS-agnostic configuration |
| [darwin](./darwin/default.nix) | macOS-specific configuration |
| [nixos](./nixos/default.nix) | NixOS-specific configuration |
| [wsl](./wsl/default.nix) | WSL-specific configuration |

View File

@ -1,9 +0,0 @@
{ ... }:
{
# AWS settings require this
permitRootLogin = "prohibit-password";
# Make sure disk size is large enough
# https://github.com/nix-community/nixos-generators/issues/150
amazonImage.sizeMB = 16 * 1024;
}

View File

@ -1,41 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
_1password = {
enable = lib.mkEnableOption {
description = "Enable 1Password.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config._1password.enable) {
unfreePackages = [
"1password"
"_1password-gui"
"1password-cli"
];
home-manager.users.${config.user} = {
home.packages = [
pkgs._1password-cli
] ++ (if pkgs.stdenv.isLinux then [ pkgs._1password-gui ] else [ ]);
};
# https://1password.community/discussion/135462/firefox-extension-does-not-connect-to-linux-app
# On Mac, does not apply: https://1password.community/discussion/142794/app-and-browser-integration
# However, the button doesn't work either:
# https://1password.community/discussion/140735/extending-support-for-trusted-web-browsers
environment.etc."1password/custom_allowed_browsers".text = ''
${
config.home-manager.users.${config.user}.programs.firefox.package
}/Applications/Firefox.app/Contents/MacOS/firefox
firefox
'';
};
}

View File

@ -1,107 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
alacritty = {
enable = lib.mkEnableOption {
description = "Enable Alacritty.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.alacritty.enable) {
home-manager.users.${config.user} = {
xsession.windowManager.i3.config.terminal = "alacritty";
programs.rofi.terminal = "${pkgs.alacritty}/bin/alacritty";
programs.alacritty = {
enable = true;
settings = {
window = {
dimensions = {
columns = 85;
lines = 30;
};
padding = {
x = 20;
y = 20;
};
opacity = 1.0;
};
scrolling.history = 10000;
font = {
size = 14.0;
};
key_bindings = [
# Used for word completion in fish_user_key_bindings
{
key = "Return";
mods = "Shift";
chars = "\\x1F";
}
# Used for searching nixpkgs in fish_user_key_bindings
{
key = "N";
mods = "Control|Shift";
chars = "\\x11F";
}
{
key = "H";
mods = "Control|Shift";
mode = "~Vi";
action = "ToggleViMode";
}
{
key = "Return";
mode = "Vi";
action = "ToggleViMode";
}
# Used to enable $ keybind in Vi mode
{
key = 5; # Scancode for key4
mods = "Shift";
mode = "Vi|~Search";
action = "Last";
}
];
colors = {
primary = {
background = config.theme.colors.base00;
foreground = config.theme.colors.base05;
};
cursor = {
text = "#1d2021";
cursor = config.theme.colors.base05;
};
normal = {
black = "#1d2021";
red = config.theme.colors.base08;
green = config.theme.colors.base0B;
yellow = config.theme.colors.base0A;
blue = config.theme.colors.base0D;
magenta = config.theme.colors.base0E;
cyan = config.theme.colors.base0C;
white = config.theme.colors.base05;
};
bright = {
black = config.theme.colors.base03;
red = config.theme.colors.base09;
green = config.theme.colors.base01;
yellow = config.theme.colors.base02;
blue = config.theme.colors.base04;
magenta = config.theme.colors.base06;
cyan = config.theme.colors.base0F;
white = config.theme.colors.base07;
};
};
draw_bold_text_with_bright_colors = false;
};
};
};
};
}

View File

@ -1,17 +0,0 @@
{ ... }:
{
imports = [
./1password.nix
./alacritty.nix
./discord.nix
./firefox.nix
./kitty.nix
./media.nix
./obsidian.nix
./qbittorrent.nix
./slack.nix
./wezterm.nix
./yt-dlp.nix
];
}

View File

@ -1,34 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
discord = {
enable = lib.mkEnableOption {
description = "Enable Discord.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.discord.enable) {
unfreePackages = [ "discord" ];
environment.systemPackages = [ pkgs.discord ];
home-manager.users.${config.user} = {
xdg.configFile."discord/settings.json".text = ''
{
"BACKGROUND_COLOR": "#202225",
"IS_MAXIMIZED": false,
"IS_MINIMIZED": false,
"OPEN_ON_STARTUP": false,
"MINIMIZE_TO_TRAY": false,
"SKIP_HOST_UPDATE": true
}
'';
};
};
}

View File

@ -1,203 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
firefox = {
enable = lib.mkEnableOption {
description = "Enable Firefox.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.firefox.enable) {
unfreePackages = [
(lib.mkIf config._1password.enable "onepassword-password-manager")
"okta-browser-plugin"
];
home-manager.users.${config.user} = {
programs.firefox = {
enable = true;
package = if pkgs.stdenv.isDarwin then pkgs.firefox-bin else pkgs.firefox;
profiles.default = {
id = 0;
name = "default";
isDefault = true;
# https://nur.nix-community.org/repos/rycee/
extensions = with pkgs.nur.repos.rycee.firefox-addons; [
(lib.mkIf config._1password.enable onepassword-password-manager)
darkreader
don-t-fuck-with-paste
facebook-container
markdownload
multi-account-containers
okta-browser-plugin
reddit-enhancement-suite
return-youtube-dislikes
sponsorblock
ublock-origin
ublacklist
vimium
];
settings = {
"app.update.auto" = false;
"browser.aboutConfig.showWarning" = false;
"browser.warnOnQuit" = false;
"browser.quitShortcut.disabled" = if pkgs.stdenv.isLinux then true else false;
"browser.theme.dark-private-windows" = true;
"browser.toolbars.bookmarks.visibility" = false;
"browser.startup.page" = 3; # Restore previous session
"browser.newtabpage.enabled" = false; # Make new tabs blank
"trailhead.firstrun.didSeeAboutWelcome" = true; # Disable welcome splash
"dom.forms.autocomplete.formautofill" = false; # Disable autofill
"extensions.formautofill.creditCards.enabled" = false; # Disable credit cards
"dom.payments.defaults.saveAddress" = false; # Disable address save
"general.autoScroll" = true; # Drag middle-mouse to scroll
"services.sync.prefs.sync.general.autoScroll" = false; # Prevent disabling autoscroll
"extensions.pocket.enabled" = false;
"toolkit.legacyUserProfileCustomizations.stylesheets" = true; # Allow userChrome.css
"layout.css.color-mix.enabled" = true;
"ui.systemUsesDarkTheme" = if config.theme.dark == true then 1 else 0;
"media.ffmpeg.vaapi.enabled" = true; # Enable hardware video acceleration
"cookiebanners.ui.desktop.enabled" = true; # Reject cookie popups
"devtools.command-button-screenshot.enabled" = true; # Scrolling screenshot of entire page
"svg.context-properties.content.enabled" = true; # Sidebery styling
"browser.tabs.hoverPreview.enabled" = false; # Disable tab previews
"browser.tabs.hoverPreview.showThumbnails" = false; # Disable tab previews
};
userChrome = ''
:root {
--focus-outline-color: ${config.theme.colors.base04} !important;
--toolbar-color: ${config.theme.colors.base07} !important;
--tab-min-height: 30px !important;
}
/* Background of tab bar */
.toolbar-items {
background-color: ${config.theme.colors.base00} !important;
}
/* Extra tab bar sides on macOS */
.titlebar-spacer {
background-color: ${config.theme.colors.base00} !important;
}
.titlebar-buttonbox-container {
background-color: ${config.theme.colors.base00} !important;
}
#tabbrowser-tabs {
border-inline-start: 0 !important;
}
/* Private Browsing indicator on macOS */
#private-browsing-indicator-with-label {
background-color: ${config.theme.colors.base00} !important;
margin-inline: 0 !important;
padding-inline: 7px;
}
/* Tabs themselves */
.tabbrowser-tab .tab-stack {
border-radius: 5px 5px 0 0;
overflow: hidden;
background-color: ${config.theme.colors.base00};
color: ${config.theme.colors.base06} !important;
}
.tab-content {
border-bottom: 2px solid color-mix(in srgb, var(--identity-tab-color) 40%, transparent);
border-radius: 5px 5px 0 0;
background-color: ${config.theme.colors.base00};
color: ${config.theme.colors.base06} !important;
}
.tab-content[selected] {
border-bottom: 2px solid color-mix(in srgb, var(--identity-tab-color) 25%, transparent);
background-color: ${config.theme.colors.base01} !important;
color: ${config.theme.colors.base07} !important;
}
/* Below tab bar */
#nav-bar {
background: ${config.theme.colors.base01} !important;
}
/* URL bar in nav bar */
#urlbar[focused=true] {
color: ${config.theme.colors.base07} !important;
background: ${config.theme.colors.base02} !important;
caret-color: ${config.theme.colors.base05} !important;
}
#urlbar:not([focused=true]) {
color: ${config.theme.colors.base04} !important;
background: ${config.theme.colors.base02} !important;
}
#urlbar ::-moz-selection {
color: ${config.theme.colors.base07} !important;
background: ${config.theme.colors.base02} !important;
}
#urlbar-input-container {
border: 1px solid ${config.theme.colors.base01} !important;
}
#urlbar-background {
background: ${config.theme.colors.base01} !important;
}
/* Text in URL bar */
#urlbar-input, #urlbar-scheme, .searchbar-textbox {
color: ${config.theme.colors.base07} !important;
}
'';
userContent = ''
@-moz-document url-prefix(about:blank) {
* {
background-color:${config.theme.colors.base01} !important;
}
}
'';
extraConfig = "";
};
};
# Mimic nixpkgs package environment for read-only profiles.ini management
# From: https://github.com/booxter/home-manager/commit/dd1602e306fec366280f5953c5e1b553e3d9672a
home.sessionVariables = {
MOZ_LEGACY_PROFILES = 1;
MOZ_ALLOW_DOWNGRADE = 1;
};
# launchd.user.envVariables = config.home-manager.users.${config.user}.home.sessionVariables;
xdg.mimeApps = {
associations.added = {
"text/html" = [ "firefox.desktop" ];
};
defaultApplications = {
"text/html" = [ "firefox.desktop" ];
};
associations.removed = {
"text/html" = [ "wine-extension-htm.desktop" ];
};
};
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.isLinux {
"${
config.home-manager.users.${config.user}.xsession.windowManager.i3.config.modifier
}+Shift+b" = "exec ${
# Don't name the script `firefox` or it will affect grep
builtins.toString (
pkgs.writeShellScript "focus-ff.sh" ''
count=$(ps aux | grep -c firefox)
if [ "$count" -eq 1 ]; then
i3-msg "exec --no-startup-id firefox"
sleep 0.5
fi
i3-msg "[class=firefox] focus"
''
)
}";
};
};
};
}

View File

@ -1,118 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
kitty = {
enable = lib.mkEnableOption {
description = "Enable Kitty.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.kitty.enable) {
# Set the Rofi-Systemd terminal for viewing logs
# Using optionalAttrs because only available in NixOS
environment =
{ }
// lib.attrsets.optionalAttrs (builtins.hasAttr "sessionVariables" config.environment) {
sessionVariables.ROFI_SYSTEMD_TERM = lib.mkDefault "${pkgs.kitty}/bin/kitty";
};
home-manager.users.${config.user} = {
# Set the i3 terminal
xsession.windowManager.i3.config.terminal = lib.mkIf pkgs.stdenv.isLinux "kitty";
# Set the Rofi terminal for running programs
programs.rofi.terminal = lib.mkIf pkgs.stdenv.isLinux (lib.mkDefault "${pkgs.kitty}/bin/kitty");
# Display images in the terminal
programs.fish.interactiveShellInit = # fish
''
if test "$TERM" = "xterm-kitty"
alias icat="kitty +kitten icat"
alias ssh="kitty +kitten ssh"
end
'';
programs.kitty = {
enable = true;
environment = { };
extraConfig = "";
font.size = 14;
keybindings = {
# Use shift+enter to complete text suggestions in fish
"shift+enter" = "send_text all \\x1F";
# Easy fullscreen toggle (for macOS)
"super+f" = "toggle_fullscreen";
};
settings = {
# Colors (adapted from: https://github.com/kdrag0n/base16-kitty/blob/master/templates/default-256.mustache)
background = config.theme.colors.base00;
foreground = config.theme.colors.base05;
selection_background = config.theme.colors.base05;
selection_foreground = config.theme.colors.base00;
url_color = config.theme.colors.base04;
cursor = config.theme.colors.base05;
active_border_color = config.theme.colors.base03;
inactive_border_color = config.theme.colors.base01;
active_tab_background = config.theme.colors.base00;
active_tab_foreground = config.theme.colors.base05;
inactive_tab_background = config.theme.colors.base01;
inactive_tab_foreground = config.theme.colors.base04;
tab_bar_background = config.theme.colors.base01;
# normal
color0 = config.theme.colors.base00;
color1 = config.theme.colors.base08;
color2 = config.theme.colors.base0B;
color3 = config.theme.colors.base0A;
color4 = config.theme.colors.base0D;
color5 = config.theme.colors.base0E;
color6 = config.theme.colors.base0C;
color7 = config.theme.colors.base05;
# bright
color8 = config.theme.colors.base03;
color9 = config.theme.colors.base08;
color10 = config.theme.colors.base0B;
color11 = config.theme.colors.base0A;
color12 = config.theme.colors.base0D;
color13 = config.theme.colors.base0E;
color14 = config.theme.colors.base0C;
color15 = config.theme.colors.base07;
# extended base16 colors
color16 = config.theme.colors.base09;
color17 = config.theme.colors.base0F;
color18 = config.theme.colors.base01;
color19 = config.theme.colors.base02;
color20 = config.theme.colors.base04;
color21 = config.theme.colors.base06;
# Scrollback
scrollback_lines = 10000;
scrollback_pager_history_size = 300; # MB
# Window
window_padding_width = 6;
tab_bar_edge = "top";
tab_bar_style = "slant";
# Disable audio
enable_audio_bell = false;
};
};
};
};
}

View File

@ -1,67 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
media = {
enable = lib.mkEnableOption {
description = "Enable media programs.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.media.enable) {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
nsxiv # Image viewer
mupdf # PDF viewer
zathura # PDF viewer
];
# Video player
programs.mpv = {
enable = true;
bindings = { };
config = {
image-display-duration = 2; # For cycling through images
hwdec = "auto-safe"; # Attempt to use GPU decoding for video
};
scripts = [
# Automatically load playlist entries before and after current file
pkgs.mpvScripts.autoload
# Delete current file after quitting
pkgs.mpvScripts.mpv-delete-file
];
};
# Set default programs for opening PDFs and other media
xdg.mimeApps = {
associations.added = {
"application/pdf" = [ "pwmt.zathura-cb.desktop" ];
"image/jpeg" = [ "nsxiv.desktop" ];
"image/png" = [ "nsxiv.desktop" ];
"image/*" = [ "nsxiv.desktop" ];
};
associations.removed = {
"application/pdf" = [
"mupdf.desktop"
"wine-extension-pdf.desktop"
];
};
defaultApplications = {
"application/pdf" = [ "pwmt.zathura-cb.desktop" ];
"image/jpeg" = [ "nsxiv.desktop" ];
"image/png" = [ "nsxiv.desktop" ];
"image/*" = [ "nsxiv.desktop" ];
};
};
};
};
}

View File

@ -1,28 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
obsidian = {
enable = lib.mkEnableOption {
description = "Enable Obsidian.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.obsidian.enable) {
unfreePackages = [ "obsidian" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ obsidian ];
};
# Broken on 2023-12-11
# https://forum.obsidian.md/t/electron-25-is-now-eol-please-upgrade-to-a-newer-version/72878/8
insecurePackages = [ "electron-25.9.0" ];
};
}

View File

@ -1,25 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
qbittorrent = {
enable = lib.mkEnableOption {
description = "Enable qBittorrent.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.qbittorrent.enable) {
home-manager.users.${config.user} = {
home.packages = with pkgs; [ qbittorrent ];
};
};
}

View File

@ -1,26 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
slack = {
enable = lib.mkEnableOption {
description = "Enable Slack.";
default = false;
};
};
};
config = lib.mkIf (config.gui.enable && config.slack.enable) {
unfreePackages = [ "slack" ];
home-manager.users.${config.user} = {
home.packages = with pkgs; [ slack ];
};
};
# Theme string: #1D2122,#665C54,#8EC07C,#BDAE93
}

View File

@ -1,286 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
wezterm = {
enable = lib.mkEnableOption {
description = "Enable WezTerm terminal.";
default = false;
};
};
};
config =
let
font = config.home-manager.users.${config.user}.programs.kitty.font.name;
in
lib.mkIf (config.gui.enable && config.wezterm.enable) {
# Set the Rofi-Systemd terminal for viewing logs
# Using optionalAttrs because only available in NixOS
environment =
{ }
// lib.attrsets.optionalAttrs (builtins.hasAttr "sessionVariables" config.environment) {
sessionVariables.ROFI_SYSTEMD_TERM = "${pkgs.wezterm}/bin/wezterm";
};
terminal = "${pkgs.wezterm}/bin/wezterm";
terminalLaunchCommand = "${config.terminal} start --";
home-manager.users.${config.user} = {
# Set the i3 terminal
xsession.windowManager.i3.config.terminal = lib.mkIf pkgs.stdenv.isLinux "wezterm";
# Display images in the terminal
programs.fish.shellAliases = {
icat = lib.mkForce "wezterm imgcat";
};
programs.wezterm = {
enable = true;
colorSchemes = {
myTheme = {
background = config.theme.colors.base00;
foreground = config.theme.colors.base05;
cursor_bg = config.theme.colors.base05;
cursor_fg = config.theme.colors.base00;
cursor_border = config.theme.colors.base05;
selection_bg = config.theme.colors.base05;
selection_fg = config.theme.colors.base00;
scrollbar_thumb = config.theme.colors.base03;
ansi = [
config.theme.colors.base01 # black
config.theme.colors.base0F # maroon
config.theme.colors.base0B # green
config.theme.colors.base0A # olive
config.theme.colors.base0D # navy
config.theme.colors.base0E # purple
config.theme.colors.base0C # teal
config.theme.colors.base06 # silver
];
brights = [
config.theme.colors.base03 # grey
config.theme.colors.base08 # red
config.theme.colors.base0B # lime
config.theme.colors.base0A # yellow
config.theme.colors.base0D # blue
config.theme.colors.base0E # fuchsia
config.theme.colors.base0C # aqua
config.theme.colors.base07 # white
];
compose_cursor = config.theme.colors.base09; # orange
copy_mode_active_highlight_bg = {
Color = config.theme.colors.base03;
};
copy_mode_active_highlight_fg = {
Color = config.theme.colors.base07;
};
copy_mode_inactive_highlight_bg = {
Color = config.theme.colors.base02;
};
copy_mode_inactive_highlight_fg = {
Color = config.theme.colors.base06;
};
quick_select_label_bg = {
Color = config.theme.colors.base02;
};
quick_select_label_fg = {
Color = config.theme.colors.base06;
};
quick_select_match_bg = {
Color = config.theme.colors.base03;
};
quick_select_match_fg = {
Color = config.theme.colors.base07;
};
};
};
extraConfig = ''
local wezterm = require("wezterm")
local config = wezterm.config_builder()
config.check_for_updates = false
config.color_scheme = "myTheme"
-- Scrollback
config.scrollback_lines = 10000
-- Window
config.window_padding = {
left = 10,
right = 10,
top = 10,
bottom = 12,
}
config.font = wezterm.font('${font}', { weight = 'Bold'})
config.font_size = ${if pkgs.stdenv.isLinux then "14.0" else "18.0"}
-- Fix color blocks instead of text
config.front_end = "WebGpu"
-- Tab Bar
config.hide_tab_bar_if_only_one_tab = true
config.window_frame = {
font = wezterm.font('${font}', { weight = 'Bold'}),
font_size = ${if pkgs.stdenv.isLinux then "12.0" else "16.0"},
}
config.colors = {
tab_bar = {
active_tab = {
bg_color = '${config.theme.colors.base00}',
fg_color = '${config.theme.colors.base04}',
},
},
}
-- Disable audio
config.audible_bell = "Disabled"
config.initial_rows = 80
config.initial_cols = 200
config.unix_domains = {
{
name = 'unix',
},
}
-- This causes `wezterm` to act as though it was started as
-- `wezterm connect unix` by default, connecting to the unix
-- domain on startup.
-- If you prefer to connect manually, leave out this line.
config.default_gui_startup_args = { 'connect', 'unix' }
config.leader = {
key = 'a',
mods = 'CTRL',
timeout_milliseconds = 2000,
}
config.keys = {
-- Attach to muxer
{
key = 'a',
mods = 'LEADER',
action = wezterm.action.AttachDomain 'unix',
},
-- Detach from muxer
{
key = 'd',
mods = 'LEADER',
action = wezterm.action.DetachDomain { DomainName = 'unix' },
},
-- sends completion string for fish autosuggestions
{
key = 'Enter',
mods = 'SHIFT',
action = wezterm.action.SendString '\x1F'
},
-- ctrl-shift-h was "hide"
{
key = 'H',
mods = 'SHIFT|CTRL',
action = wezterm.action.DisableDefaultAssignment
},
-- alt-enter was "fullscreen"
{
key = 'Enter',
mods = 'ALT',
action = wezterm.action.DisableDefaultAssignment
},
-- make super-f "fullscreen"
{
key = 'f',
mods = 'SUPER',
action = wezterm.action.ToggleFullScreen
},
-- super-t open new tab in new dir
{
key = 't',
mods = ${if pkgs.stdenv.isDarwin then "'SUPER'" else "'ALT'"},
action = wezterm.action.SpawnCommandInNewTab {
cwd = wezterm.home_dir,
},
},
-- shift-super-t open new tab in same dir
{
key = 't',
mods = 'SUPER|SHIFT',
action = wezterm.action.SpawnTab 'CurrentPaneDomain'
},
-- project switcher
{
key = 'P',
mods = 'SUPER',
action = wezterm.action_callback(function(window, pane)
local choices = {}
wezterm.log_info "working?"
function scandir(directory)
local i, t, popen = 0, {}, io.popen
local pfile = popen('${pkgs.fd}/bin/fd --search-path "'..directory..'" --type directory --exact-depth 2 | ${pkgs.proximity-sort}/bin/proximity-sort "'..os.getenv("HOME").."/dev/work"..'"')
for filename in pfile:lines() do
i = i + 1
t[i] = filename
end
pfile:close()
return t
end
for _, v in pairs(scandir(os.getenv("HOME").."/dev")) do
table.insert(choices, { label = v })
end
window:perform_action(
wezterm.action.InputSelector {
action = wezterm.action_callback(function(window, pane, id, label)
if not id and not label then
wezterm.log_info "cancelled"
else
window:perform_action(
wezterm.action.SpawnCommandInNewTab {
cwd = label,
},
pane
)
end
end),
fuzzy = true,
title = "Select Project",
choices = choices,
},
pane
)
end),
},
}
-- print the workspace name at the upper right
wezterm.on("update-right-status", function(window, pane)
window:set_right_status(window:active_workspace())
end)
-- load plugin
local workspace_switcher = wezterm.plugin.require("https://github.com/MLFlexer/smart_workspace_switcher.wezterm")
-- set path to zoxide
workspace_switcher.zoxide_path = "${pkgs.zoxide}/bin/zoxide"
-- keymaps
table.insert(config.keys, { key = "s", mods = "CTRL|SHIFT", action = workspace_switcher.switch_workspace() })
-- table.insert(config.keys, { key = "t", mods = "CTRL|SHIFT", action = wezterm.action.ShowLauncherArgs({ flags = "FUZZY|WORKSPACES" }) })
table.insert(config.keys, { key = "[", mods = "CTRL|SHIFT", action = wezterm.action.SwitchWorkspaceRelative(1) })
table.insert(config.keys, { key = "]", mods = "CTRL|SHIFT", action = wezterm.action.SwitchWorkspaceRelative(-1) })
return config
'';
};
};
};
}

View File

@ -1,38 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options = {
yt-dlp = {
enable = lib.mkEnableOption {
description = "Enable YouTube downloader.";
default = false;
};
};
};
config = lib.mkIf (config.yt-dlp.enable) {
home-manager.users.${config.user} = {
programs.yt-dlp = {
enable = true;
extraConfig = "";
settings = {
no-continue = true; # Always re-download each fragment
no-overwrites = true; # Don't overwrite existing files
download-archive = "archive.log"; # Log of archives
embed-metadata = true;
embed-thumbnail = true;
embed-subs = true;
sub-langs = "en.*";
concurrent-fragments = 4; # Parallel download chunks
};
};
programs.fish.shellAbbrs.yt = "yt-dlp";
};
};
}

View File

@ -1,200 +0,0 @@
{
config,
lib,
pkgs,
...
}:
{
imports = [
./applications
./mail
./neovim
./programming
./repositories
./shell
];
options = {
user = lib.mkOption {
type = lib.types.str;
description = "Primary user of the system";
};
fullName = lib.mkOption {
type = lib.types.str;
description = "Human readable name of the user";
};
userDirs = {
# Required to prevent infinite recursion when referenced by himalaya
download = lib.mkOption {
type = lib.types.str;
description = "XDG directory for downloads";
default = if pkgs.stdenv.isDarwin then "$HOME/Downloads" else "$HOME/downloads";
};
};
identityFile = lib.mkOption {
type = lib.types.str;
description = "Path to existing private key file.";
default = "/etc/ssh/ssh_host_ed25519_key";
};
gui = {
enable = lib.mkEnableOption {
description = "Enable graphics.";
default = false;
};
};
theme = {
colors = lib.mkOption {
type = lib.types.attrs;
description = "Base16 color scheme.";
default = (import ../colorscheme/gruvbox).dark;
};
dark = lib.mkOption {
type = lib.types.bool;
description = "Enable dark mode.";
default = true;
};
};
homePath = lib.mkOption {
type = lib.types.path;
description = "Path of user's home directory.";
default = builtins.toPath (
if pkgs.stdenv.isDarwin then "/Users/${config.user}" else "/home/${config.user}"
);
};
dotfilesPath = lib.mkOption {
type = lib.types.path;
description = "Path of dotfiles repository.";
default = config.homePath + "/dev/personal/dotfiles";
};
dotfilesRepo = lib.mkOption {
type = lib.types.str;
description = "Link to dotfiles repository HTTPS URL.";
};
unfreePackages = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "List of unfree packages to allow.";
default = [ ];
};
insecurePackages = lib.mkOption {
type = lib.types.listOf lib.types.str;
description = "List of insecure packages to allow.";
default = [ ];
};
hostnames = {
audiobooks = lib.mkOption {
type = lib.types.str;
description = "Hostname for audiobook server (Audiobookshelf).";
};
budget = lib.mkOption {
type = lib.types.str;
description = "Hostname for budgeting server (ActualBudget).";
};
files = lib.mkOption {
type = lib.types.str;
description = "Hostname for files server (Filebrowser).";
};
git = lib.mkOption {
type = lib.types.str;
description = "Hostname for git server (Gitea).";
};
metrics = lib.mkOption {
type = lib.types.str;
description = "Hostname for metrics server.";
};
minecraft = lib.mkOption {
type = lib.types.str;
description = "Hostname for Minecraft server.";
};
paperless = lib.mkOption {
type = lib.types.str;
description = "Hostname for document server (paperless-ngx).";
};
photos = lib.mkOption {
type = lib.types.str;
description = "Hostname for photo management (Immich).";
};
prometheus = lib.mkOption {
type = lib.types.str;
description = "Hostname for Prometheus server.";
};
influxdb = lib.mkOption {
type = lib.types.str;
description = "Hostname for InfluxDB2 server.";
};
secrets = lib.mkOption {
type = lib.types.str;
description = "Hostname for passwords and secrets (Vaultwarden).";
};
stream = lib.mkOption {
type = lib.types.str;
description = "Hostname for video/media library (Jellyfin).";
};
content = lib.mkOption {
type = lib.types.str;
description = "Hostname for personal content system (Nextcloud).";
};
books = lib.mkOption {
type = lib.types.str;
description = "Hostname for books library (Calibre-Web).";
};
download = lib.mkOption {
type = lib.types.str;
description = "Hostname for download services.";
};
irc = lib.mkOption {
type = lib.types.str;
description = "Hostname for IRC services.";
};
n8n = lib.mkOption {
type = lib.types.str;
description = "Hostname for n8n automation.";
};
notifications = lib.mkOption {
type = lib.types.str;
description = "Hostname for push notification services (ntfy).";
};
status = lib.mkOption {
type = lib.types.str;
description = "Hostname for status page (Uptime-Kuma).";
};
transmission = lib.mkOption {
type = lib.types.str;
description = "Hostname for peer2peer downloads (Transmission).";
};
};
};
config =
let
stateVersion = "23.05";
in
{
# Basic common system packages for all devices
environment.systemPackages = with pkgs; [
git
vim
wget
curl
];
# Use the system-level nixpkgs instead of Home Manager's
home-manager.useGlobalPkgs = true;
# Install packages to /etc/profiles instead of ~/.nix-profile, useful when
# using multiple profiles for one user
home-manager.useUserPackages = true;
# Allow specified unfree packages (identified elsewhere)
# Retrieves package object based on string name
nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) config.unfreePackages;
# Allow specified insecure packages (identified elsewhere)
nixpkgs.config.permittedInsecurePackages = config.insecurePackages;
# Pin a state version to prevent warnings
home-manager.users.${config.user}.home.stateVersion = stateVersion;
home-manager.users.root.home.stateVersion = stateVersion;
};
}

View File

@ -1,220 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.mail.aerc.enable = lib.mkEnableOption "Aerc email.";
config = lib.mkIf config.mail.aerc.enable {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
w3m # Render HTML
dante # Socksify for rendering HTML
];
programs.aerc = {
enable = true;
extraBinds = {
# Binds are of the form <key sequence> = <command to run>
# To use '=' in a key sequence, substitute it with "Eq": "<Ctrl+Eq>"
# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit
global = {
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab <Enter>";
"<C-t>" = ":term<Enter>";
"?" = ":help keys<Enter>";
};
messages = {
q = ":quit<Enter>";
j = ":next <Enter>";
"<Down>" = ":next<Enter>";
"<C-d>" = ":next 50%<Enter>";
"<C-f>" = ":next 100%<Enter>";
"<PgDn>" = ":next 100%<Enter>";
k = ":prev <Enter>";
"<Up>" = ":prev<Enter>";
"<C-u>" = ":prev 50%<Enter>";
"<C-b>" = ":prev 100%<Enter>";
"<PgUp>" = ":prev 100%<Enter>";
g = ":select 0 <Enter>";
G = ":select -1<Enter>";
J = ":next-folder <Enter>";
K = ":prev-folder<Enter>";
H = ":collapse-folder<Enter>";
L = ":expand-folder<Enter>";
v = ":mark -t<Enter>";
V = ":mark -v<Enter>";
T = ":toggle-threads<Enter>";
"<Enter>" = ":view<Enter>";
d = ":prompt 'Really delete this message?' 'delete-message'<Enter>";
D = ":move Trash<Enter>";
A = ":archive flat<Enter>";
C = ":compose<Enter>";
rr = ":reply -a<Enter>";
rq = ":reply -aq<Enter>";
Rr = ":reply<Enter>";
Rq = ":reply -q<Enter>";
c = ":cf<space>";
"$" = ":term<space>";
"!" = ":term<space>";
"|" = ":pipe<space>";
"/" = ":search<space>-a<space>";
"\\" = ":filter <space>";
n = ":next-result<Enter>";
N = ":prev-result<Enter>";
"<Esc>" = ":clear<Enter>";
};
"messages:folder=Drafts" = {
"<Enter>" = ":recall<Enter>";
};
view = {
"/" = ":toggle-key-passthrough <Enter> /";
q = ":close<Enter>";
O = ":open<Enter>";
S = ":save<space>";
"|" = ":pipe<space>";
D = ":move Trash<Enter>";
A = ":archive flat<Enter>";
"<C-l>" = ":open-link <space>";
f = ":forward <Enter>";
rr = ":reply -a<Enter>";
rq = ":reply -aq<Enter>";
Rr = ":reply<Enter>";
Rq = ":reply -q<Enter>";
H = ":toggle-headers<Enter>";
"<C-k>" = ":prev-part<Enter>";
"<C-j>" = ":next-part<Enter>";
J = ":next <Enter>";
K = ":prev<Enter>";
};
"view::passthrough" = {
"$noinherit" = "true";
"$ex" = "<C-x>";
"<Esc>" = ":toggle-key-passthrough<Enter>";
};
compose = {
# Keybindings used when the embedded terminal is not selected in the compose
# view
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-k>" = ":prev-field<Enter>";
"<C-j>" = ":next-field<Enter>";
"<A-p>" = ":switch-account -p<Enter>";
"<A-n>" = ":switch-account -n<Enter>";
"<tab>" = ":next-field<Enter>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
"compose::editor" = {
# Keybindings used when the embedded terminal is selected in the compose view
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-k>" = ":prev-field<Enter>";
"<C-j>" = ":next-field<Enter>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
"compose::review" = {
# Keybindings used when reviewing a message to be sent
y = ":send <Enter>";
n = ":abort<Enter>";
p = ":postpone<Enter>";
q = ":choose -o d discard abort -o p postpone postpone<Enter>";
e = ":edit<Enter>";
a = ":attach<space>";
d = ":detach<space>";
};
terminal = {
"$noinherit" = "true";
"$ex" = "<C-x>";
"<C-p>" = ":prev-tab<Enter>";
"<C-n>" = ":next-tab<Enter>";
};
};
extraConfig = {
general = {
unsafe-accounts-conf = true;
# log-file = "~/.cache/aerc.log";
# log-level = "debug";
};
viewer = {
pager = "${pkgs.less}/bin/less -R";
};
filters = {
"text/plain" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
"text/calendar" = "${pkgs.gawk}/bin/awk -f ${pkgs.aerc}/libexec/aerc/filters/calendar";
"text/html" =
"${pkgs.aerc}/libexec/aerc/filters/html | ${pkgs.aerc}/libexec/aerc/filters/colorize"; # Requires w3m, dante
# "text/*" =
# ''${pkgs.bat}/bin/bat -fP --file-name="$AERC_FILENAME "'';
"message/delivery-status" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
"message/rfc822" = "${pkgs.aerc}/libexec/aerc/filters/colorize";
"application/x-sh" = "${pkgs.bat}/bin/bat -fP -l sh";
"application/pdf" = "${pkgs.zathura}/bin/zathura -";
"audio/*" = "${pkgs.mpv}/bin/mpv -";
"image/*" = "${pkgs.feh}/bin/feh -";
};
};
};
accounts.email.accounts.home.aerc = {
enable = true;
extraAccounts = {
check-mail = "5m";
check-mail-cmd = "${pkgs.isync}/bin/mbsync -a";
check-mail-timeout = "15s";
};
};
xdg.desktopEntries.aerc = lib.mkIf (pkgs.stdenv.isLinux && config.gui.enable) {
name = "aerc";
exec = "${config.terminalLaunchCommand} aerc %u";
};
xsession.windowManager.i3.config.keybindings = lib.mkIf pkgs.stdenv.isLinux {
"${config.home-manager.users.${config.user}.xsession.windowManager.i3.config.modifier}+Shift+e" =
"exec ${
# Don't name the script `aerc` or it will affect grep
builtins.toString (
pkgs.writeShellScript "focus-mail.sh" ''
count=$(ps aux | grep -c aerc)
if [ "$count" -eq 1 ]; then
i3-msg "exec --no-startup-id ${config.terminal} start --class aerc -- aerc"
sleep 0.25
fi
i3-msg "[class=aerc] focus"
''
)
}";
};
programs.fish.shellAbbrs = {
ae = "aerc";
};
};
};
}

View File

@ -1,139 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
imports = [
./himalaya.nix
./aerc.nix
./system.nix
];
options = {
mail.enable = lib.mkEnableOption "Mail service.";
mail.user = lib.mkOption {
type = lib.types.str;
description = "User name for the email address.";
default = config.user;
};
mail.server = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server name for the email address.";
};
mail.imapHost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server host for IMAP (reading mail).";
};
mail.smtpHost = lib.mkOption {
type = lib.types.nullOr lib.types.str;
description = "Server host for SMTP (sending mail).";
};
};
config = lib.mkIf config.mail.enable {
home-manager.users.${config.user} = {
programs.mbsync = {
enable = true;
};
# Automatically check for mail and keep files synced locally
services.mbsync = lib.mkIf pkgs.stdenv.isLinux {
enable = true;
frequency = "*:0/5";
postExec = "${pkgs.notmuch}/bin/notmuch new";
};
# Used to watch for new mail and trigger sync
services.imapnotify.enable = pkgs.stdenv.isLinux;
# Allows sending email from CLI/sendmail
programs.msmtp.enable = true;
# Better local mail search
programs.notmuch = {
enable = true;
new.ignore = [
".mbsyncstate.lock"
".mbsyncstate.journal"
".mbsyncstate.new"
];
};
accounts.email = {
# Where email files are stored
maildirBasePath = "${config.homePath}/mail";
accounts = {
home =
let
address = "${config.mail.user}@${config.mail.server}";
in
{
userName = address;
realName = config.fullName;
primary = true;
inherit address;
aliases = map (user: "${user}@${config.mail.server}") [
"me"
"hey"
"admin"
];
# Options for contact completion
alot = { };
imap = {
host = config.mail.imapHost;
port = 993;
tls.enable = true;
};
# Watch for mail and run notifications or sync
imapnotify = {
enable = true;
boxes = [ "Inbox" ];
onNotify = "${pkgs.isync}/bin/mbsync -a";
onNotifyPost =
lib.mkIf config.home-manager.users.${config.user}.services.dunst.enable
"${pkgs.libnotify}/bin/notify-send 'New mail arrived'";
};
# Name of the directory in maildir for this account
maildir = {
path = "main";
};
# Bi-directional syncing options for local files
mbsync = {
enable = true;
create = "both";
expunge = "both";
remove = "both";
patterns = [ "*" ];
extraConfig.channel = {
CopyArrivalDate = "yes"; # Sync time of original message
};
};
# Enable indexing
notmuch.enable = true;
# Used to login and send and receive emails
passwordCommand = "${pkgs.age}/bin/age --decrypt --identity ~/.ssh/id_ed25519 ${pkgs.writeText "mailpass.age" (builtins.readFile ../../../private/mailpass.age)}";
smtp = {
host = config.mail.smtpHost;
port = 465;
tls.enable = true;
};
};
};
};
};
};
}

View File

@ -1,26 +0,0 @@
{ config, lib, ... }:
{
options.mail.himalaya.enable = lib.mkEnableOption "Himalaya email.";
config = lib.mkIf config.mail.himalaya.enable {
home-manager.users.${config.user} = {
programs.himalaya = {
enable = true;
};
accounts.email.accounts.home.himalaya = {
enable = true;
settings = {
downloads-dir = config.userDirs.download;
smtp-insecure = true;
};
};
programs.fish.shellAbbrs = {
hi = "himalaya";
};
};
};
}

View File

@ -1,35 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
config = lib.mkIf (config.mail.enable || config.server) {
home-manager.users.${config.user} = {
programs.msmtp.enable = true;
# The system user for sending automatic notifications
accounts.email.accounts.system =
let
address = "system@${config.mail.server}";
in
{
userName = address;
realName = "NixOS System";
primary = !config.mail.enable; # Only primary if mail not enabled
inherit address;
passwordCommand = "${pkgs.age}/bin/age --decrypt --identity ${config.identityFile} ${pkgs.writeText "mailpass-system.age" (builtins.readFile ../../../private/mailpass-system.age)}";
msmtp.enable = true;
smtp = {
host = config.mail.smtpHost;
port = 465;
tls.enable = true;
};
};
};
};
}

View File

@ -1,13 +0,0 @@
{ pkgs, ... }:
{
# Plugin for aligning text programmatically
plugins = [ pkgs.vimPlugins.tabular ];
lua = ''
-- Align
vim.keymap.set("", "<Leader>ta", ":Tabularize /")
vim.keymap.set("", "<Leader>t#", ":Tabularize /#<CR>")
vim.keymap.set("", "<Leader>tl", ":Tabularize /---<CR>")
'';
}

View File

@ -1,30 +0,0 @@
{ pkgs, ... }:
{
# Shows buffers in a VSCode-style tab layout
plugins = [
pkgs.vimPlugins.bufferline-nvim
pkgs.vimPlugins.vim-bbye # Better closing of buffers
pkgs.vimPlugins.snipe-nvim # Jump between open buffers
];
setup.bufferline = {
options = {
diagnostics = "nvim_lsp";
always_show_bufferline = false;
separator_style = "slant";
offsets = [ { filetype = "NvimTree"; } ];
};
};
setup.snipe = { };
lua = ''
-- Move buffers
vim.keymap.set("n", "L", ":BufferLineCycleNext<CR>", { silent = true })
vim.keymap.set("n", "H", ":BufferLineCyclePrev<CR>", { silent = true })
-- Kill buffer
vim.keymap.set("n", "<Leader>x", " :Bdelete<CR>", { silent = true })
-- Jump to buffer
vim.keymap.set("n", "gb", require("snipe").open_buffer_menu, { silent = true }) '';
}

View File

@ -1,27 +0,0 @@
{
pkgs,
lib,
config,
...
}:
{
# Sets Neovim colors based on Nix colorscheme
options.colors = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
description = "Attrset of base16 colorscheme key value pairs.";
};
config = {
plugins = [ pkgs.vimPlugins.base16-nvim ];
setup.base16-colorscheme = config.colors;
# Telescope isn't working, shut off for now
lua = ''
require('base16-colorscheme').with_config {
telescope = false,
}
'';
};
}

View File

@ -1,183 +0,0 @@
{ pkgs, dsl, ... }:
{
plugins = [
pkgs.vimPlugins.nvim-cmp
pkgs.vimPlugins.cmp-nvim-lsp
pkgs.vimPlugins.cmp-buffer
pkgs.vimPlugins.cmp-path
pkgs.vimPlugins.cmp-cmdline
pkgs.vimPlugins.cmp-nvim-lua
pkgs.vimPlugins.luasnip
pkgs.vimPlugins.cmp_luasnip
pkgs.vimPlugins.cmp-rg
];
use.cmp.setup = dsl.callWith {
# Disable in telescope buffers
enabled = dsl.rawLua ''
function()
if vim.bo.buftype == "prompt" then
return false
end
return true
end
'';
# Basic completion keybinds
mapping = {
"['<C-n>']" =
dsl.rawLua "require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })";
"['<C-p>']" =
dsl.rawLua "require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })";
"['<Down>']" =
dsl.rawLua "require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Select }, { 'i', 'c' })";
"['<Up>']" =
dsl.rawLua "require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Select }, { 'i', 'c' })";
"['<C-d>']" = dsl.rawLua "require('cmp').mapping.scroll_docs(-4)";
"['<C-f>']" = dsl.rawLua "require('cmp').mapping.scroll_docs(4)";
"['<C-e>']" = dsl.rawLua "require('cmp').mapping.abort()";
"['<C-y>']" =
dsl.rawLua "require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })";
"['<C-r>']" =
dsl.rawLua "require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })";
"['<Esc>']" = dsl.rawLua ''
function(_)
cmp.mapping({
i = cmp.mapping.abort(),
c = cmp.mapping.close(),
})
vim.cmd("stopinsert") --- Abort and leave insert mode
end
'';
"['<C-k>']" = dsl.rawLua ''
cmp.mapping(function(_)
if require("luasnip").expand_or_jumpable() then
require("luasnip").expand_or_jump()
end
end, { "i", "s" })
'';
};
# These are where the completion engine gets its suggestions
sources = [
{ name = "nvim_lua"; } # Fills in common Neovim lua functions
{ name = "nvim_lsp"; } # LSP results
{ name = "path"; } # Shell completion from current PATH
{
name = "buffer"; # Grep for text from the current text buffer
keyword_length = 3;
max_item_count = 10;
}
{
name = "rg"; # Grep for text from the current directory
keyword_length = 6;
max_item_count = 10;
option = {
additional_arguments = "--ignore-case";
};
}
];
# Styling of the completion menu
formatting = {
fields = [
"kind"
"abbr"
"menu"
];
format = dsl.rawLua ''
function(entry, vim_item)
local kind_icons = {
Text = "",
Method = "m",
Function = "",
Constructor = "",
Field = "",
Variable = "",
Class = "",
Interface = "",
Module = "",
Property = "",
Unit = "",
Value = "",
Enum = "",
Keyword = "",
Snippet = "",
Color = "",
File = "",
Reference = "",
Folder = "",
EnumMember = "",
Constant = "",
Struct = "",
Event = "",
Operator = "",
TypeParameter = "",
}
vim_item.kind = string.format("%s", kind_icons[vim_item.kind])
vim_item.menu = ({
buffer = "[Buffer]",
path = "[Path]",
rg = "[Grep]",
nvim_lsp = "[LSP]",
nvim_lua = "[Lua]",
})[entry.source.name]
return vim_item
end
'';
};
experimental = {
native_menu = false; # Use cmp menu instead of Vim menu
ghost_text = true; # Show preview auto-completion
};
};
lua' = ''
-- Use buffer source for `/`
require('cmp').setup.cmdline("/", {
mapping = {
['<C-n>'] = {
c = require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-p>'] = {
c = require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-y>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })
},
['<C-r>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })
},
},
sources = require('cmp').config.sources({
{ name = "buffer", keyword_length = 5 },
}),
})
-- Use cmdline & path source for ':'
require('cmp').setup.cmdline(":", {
mapping = {
['<C-n>'] = {
c = require('cmp').mapping.select_next_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-p>'] = {
c = require('cmp').mapping.select_prev_item({ behavior = require('cmp').SelectBehavior.Insert }, { 'i', 'c' })
},
['<C-y>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Insert, select = true, }, { 'i', 'c' })
},
['<C-r>'] = {
c = require('cmp').mapping.confirm({ behavior = require('cmp').ConfirmBehavior.Replace, select = true, }, { 'i', 'c' })
},
},
sources = require('cmp').config.sources({
{ name = "path" },
}, {
{ name = "cmdline" },
}),
})
'';
}

View File

@ -1,17 +0,0 @@
-- Keymap to open file in GitHub web
vim.keymap.set("n", "<Leader>gr", ":!gh browse %<CR><CR>", { silent = true })
-- Pop a terminal to watch the current run
local gitwatch = require("toggleterm.terminal").Terminal:new({
cmd = "fish --interactive --init-command 'gh run watch'",
hidden = true,
direction = "float",
})
-- Set a toggle for this terminal
function GITWATCH_TOGGLE()
gitwatch:toggle()
end
-- Keymap to toggle the run
vim.keymap.set("n", "<Leader>W", GITWATCH_TOGGLE)

View File

@ -1,35 +0,0 @@
vim.keymap.set("", "<Space>", "<Nop>", { silent = true })
vim.g.mapleader = " "
vim.g.maplocalleader = " "
local gitsigns = require("gitsigns")
vim.keymap.set("n", "<Leader>gB", gitsigns.blame_line)
vim.keymap.set("n", "<Leader>gp", gitsigns.preview_hunk)
vim.keymap.set("v", "<Leader>gp", gitsigns.preview_hunk)
vim.keymap.set("n", "<Leader>gd", gitsigns.diffthis)
vim.keymap.set("v", "<Leader>gd", gitsigns.diffthis)
vim.keymap.set("n", "<Leader>rgf", gitsigns.reset_buffer)
vim.keymap.set("v", "<Leader>hs", gitsigns.stage_hunk)
vim.keymap.set("n", "<Leader>hr", gitsigns.reset_hunk)
vim.keymap.set("v", "<Leader>hr", gitsigns.reset_hunk)
-- Navigation
vim.keymap.set("n", "]g", function()
if vim.wo.diff then
return "]g"
end
vim.schedule(function()
gitsigns.next_hunk()
end)
return "<Ignore>"
end, { expr = true })
vim.keymap.set("n", "[g", function()
if vim.wo.diff then
return "[g"
end
vim.schedule(function()
gitsigns.prev_hunk()
end)
return "<Ignore>"
end, { expr = true })

View File

@ -1,6 +0,0 @@
{ pkgs, ... }:
{
plugins = [ pkgs.vimPlugins.gitsigns-nvim ];
setup.gitsigns = { };
lua = builtins.readFile ./gitsigns.lua;
}

View File

@ -1,6 +0,0 @@
local k9s = require("toggleterm.terminal").Terminal:new({ cmd = "k9s" })
function K9S_TOGGLE()
k9s:toggle()
end
vim.keymap.set("n", "<Leader>9", K9S_TOGGLE)

View File

@ -1,10 +0,0 @@
vim.keymap.set("n", "gd", vim.lsp.buf.definition)
vim.keymap.set("n", "gT", vim.lsp.buf.type_definition)
vim.keymap.set("n", "gi", vim.lsp.buf.implementation)
vim.keymap.set("n", "gh", vim.lsp.buf.hover)
-- vim.keymap.set("n", "gr", telescope.lsp_references)
vim.keymap.set("n", "<Leader>R", vim.lsp.buf.rename)
vim.keymap.set("n", "]e", vim.diagnostic.goto_next)
vim.keymap.set("n", "[e", vim.diagnostic.goto_prev)
vim.keymap.set("n", "<Leader>de", vim.diagnostic.open_float)
vim.keymap.set("n", "<Leader>E", vim.lsp.buf.code_action)

View File

@ -1,146 +0,0 @@
{
pkgs,
lib,
config,
dsl,
...
}:
{
# Terraform optional because non-free
options.terraform = lib.mkEnableOption "Whether to enable Terraform LSP";
options.github = lib.mkEnableOption "Whether to enable GitHub features";
options.kubernetes = lib.mkEnableOption "Whether to enable Kubernetes features";
config = {
plugins = [
pkgs.vimPlugins.nvim-lspconfig
pkgs.vimPlugins.conform-nvim
pkgs.vimPlugins.fidget-nvim
pkgs.vimPlugins.nvim-lint
pkgs.vimPlugins.vim-table-mode
pkgs.vimPlugins.tiny-inline-diagnostic-nvim
];
setup.fidget = { };
setup.tiny-inline-diagnostic = { };
use.lspconfig.lua_ls.setup = dsl.callWith {
settings = {
Lua = {
diagnostics = {
globals = [
"vim"
"hs"
];
};
};
};
capabilities = dsl.rawLua "require('cmp_nvim_lsp').default_capabilities()";
cmd = [ "${pkgs.lua-language-server}/bin/lua-language-server" ];
};
use.lspconfig.nixd.setup = dsl.callWith {
cmd = [ "${pkgs.nixd}/bin/nixd" ];
capabilities = dsl.rawLua "require('cmp_nvim_lsp').default_capabilities()";
};
use.lspconfig.pyright.setup = dsl.callWith {
cmd = [
"${pkgs.pyright}/bin/pyright-langserver"
"--stdio"
];
};
use.lspconfig.terraformls.setup = dsl.callWith {
cmd =
if config.terraform then
[
"${pkgs.terraform-ls}/bin/terraform-ls"
"serve"
]
else
[ "echo" ];
};
use.lspconfig.rust_analyzer.setup = dsl.callWith {
cmd = [ "${pkgs.rust-analyzer}/bin/rust-analyzer" ];
settings = {
"['rust-analyzer']" = {
check = {
command = "clippy";
};
files = {
excludeDirs = [ ".direnv" ];
};
cargo = {
features = "all";
};
};
};
};
setup.conform = {
format_on_save = {
# These options will be passed to conform.format()
timeout_ms = 1500;
lsp_fallback = true;
};
formatters_by_ft = {
lua = [ "stylua" ];
python = [ "black" ];
fish = [ "fish_indent" ];
nix = [ "nixfmt" ];
rust = [ "rustfmt" ];
sh = [ "shfmt" ];
terraform = if config.terraform then [ "terraform_fmt" ] else [ ];
hcl = [ "hcl" ];
};
formatters = {
lua.command = "${pkgs.stylua}/bin/stylua";
black.command = "${pkgs.black}/bin/black";
fish_indent.command = "${pkgs.fish}/bin/fish_indent";
nixfmt.command = "${pkgs.nixfmt-rfc-style}/bin/nixfmt";
rustfmt.command = "${pkgs.rustfmt}/bin/rustfmt";
shfmt = {
command = "${pkgs.shfmt}/bin/shfmt";
prepend_args = [
"-i"
"4"
"-ci"
];
};
terraform_fmt.command = if config.terraform then "${pkgs.terraform}/bin/terraform" else "";
hcl.command = "${pkgs.hclfmt}/bin/hclfmt";
};
};
use.lint = {
linters_by_ft = dsl.toTable {
python = [ "ruff" ];
sh = [ "shellcheck" ];
};
};
vim.api.nvim_create_autocmd = dsl.callWith [
(dsl.toTable [
"BufEnter"
"BufWritePost"
])
(dsl.rawLua "{ callback = function() require('lint').try_lint() end }")
];
lua = ''
${builtins.readFile ./lsp.lua}
local ruff = require('lint').linters.ruff; ruff.cmd = "${pkgs.ruff}/bin/ruff"
local shellcheck = require('lint').linters.shellcheck; shellcheck.cmd = "${pkgs.shellcheck}/bin/shellcheck"
-- Prevent infinite log size (change this when debugging)
vim.lsp.set_log_level("off")
-- Hide buffer diagnostics (use tiny-inline-diagnostic.nvim instead)
vim.diagnostic.config({ virtual_text = false })
'';
};
}

View File

@ -1,86 +0,0 @@
{
pkgs,
dsl,
lib,
...
}:
{
plugins = [
pkgs.vimPlugins.vim-surround # Keybinds for surround characters
pkgs.vimPlugins.vim-eunuch # File manipulation commands
pkgs.vimPlugins.vim-fugitive # Git commands
pkgs.vimPlugins.vim-repeat # Better repeat using .
pkgs.vimPlugins.vim-abolish # Keep capitalization in substitute (Subvert)
pkgs.vimPlugins.markview-nvim # Markdown preview
pkgs.vimPlugins.nvim-colorizer-lua # Hex color previews
pkgs.vimPlugins.which-key-nvim # Keybind helper
];
# Initialize some plugins
setup.colorizer = {
user_default_options = {
names = false;
};
};
setup.markview = { };
setup.which-key = { };
vim.o = {
termguicolors = true; # Set to truecolor
hidden = true; # Don't unload buffers when leaving them
list = true; # Reveal whitespace with dashes
expandtab = true; # Tabs into spaces
shiftwidth = 4; # Amount to shift with > key
softtabstop = 4; # Amount to shift with <TAB> key
ignorecase = true; # Ignore case when searching
smartcase = true; # Check case when using capitals in search
infercase = true; # Don't match cases when completing suggestions
incsearch = true; # Search while typing
visualbell = true; # No sounds
scrolljump = 1; # Number of lines to scroll
scrolloff = 3; # Margin of lines to see while scrolling
splitright = true; # Vertical splits on the right side
splitbelow = true; # Horizontal splits on the bottom side
clipboard = "unnamedplus"; # Uses system clipboard for yanking
updatetime = 300; # Faster diagnostics
mouse = "nv"; # Mouse interaction / scrolling
inccommand = "split"; # Live preview search and replace
};
vim.wo = {
number = true; # Show line numbers
relativenumber = true; # Relative numbers instead of absolute
};
# For which-key-nvim
vim.o.timeout = true;
vim.o.timeoutlen = 300;
# Better backup, swap and undo storage
vim.o.backup = true; # Easier to recover and more secure
vim.opt.undofile = true; # Keeps undos after quit
vim.opt.swapfile = false; # Instead of swaps, create backups
vim.o.backupdir = dsl.rawLua ''vim.fn.expand("~/.local/state/nvim/backup//")'';
vim.o.undodir = dsl.rawLua ''vim.fn.expand("~/.local/state/nvim/undo//")'';
# Required for nvim-cmp completion
vim.opt.completeopt = [
"menu"
"menuone"
"noselect"
];
lua = lib.mkBefore ''
vim.loader.enable()
${builtins.readFile ../lua/keybinds.lua};
${builtins.readFile ../lua/settings.lua};
'';
vimscript = ''
" Remember last position when reopening file
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$") | exe "normal! g`\"" | endif
" Flash highlight when yanking
au TextYankPost * silent! lua vim.highlight.on_yank { timeout = 250 }
'';
}

View File

@ -1,10 +0,0 @@
{ pkgs, ... }:
{
plugins = [ pkgs.vimPlugins.lualine-nvim ];
setup.lualine = {
options = {
theme = "base16";
icons_enabled = true;
};
};
}

View File

@ -1,80 +0,0 @@
{ pkgs, lib, ... }:
{
plugins = [
(pkgs.vimPlugins.nvim-treesitter.withPlugins (
_plugins: with pkgs.tree-sitter-grammars; [
tree-sitter-bash
tree-sitter-c
tree-sitter-fish
tree-sitter-hcl
tree-sitter-ini
tree-sitter-json
tree-sitter-lua
tree-sitter-markdown
tree-sitter-markdown-inline
tree-sitter-nix
tree-sitter-puppet
tree-sitter-python
tree-sitter-rasi
tree-sitter-toml
tree-sitter-vimdoc
tree-sitter-yaml
]
))
pkgs.vimPlugins.vim-matchup # Better % jumping in languages
pkgs.vimPlugins.playground # Tree-sitter experimenting
pkgs.vimPlugins.nginx-vim
pkgs.vimPlugins.vim-helm
# pkgs.vimPlugins.hmts-nvim # Tree-sitter injections for home-manager
(pkgs.vimUtils.buildVimPlugin {
pname = "nmasur";
version = "0.1";
src = ../plugin;
})
];
setup."nvim-treesitter.configs" = {
highlight = {
enable = true;
};
indent = {
enable = true;
};
matchup = {
enable = true;
}; # Uses vim-matchup
textobjects = {
select = {
enable = true;
lookahead = true; # Jump forward automatically
keymaps = {
"['af']" = "@function.outer";
"['if']" = "@function.inner";
"['ac']" = "@class.outer";
"['ic']" = "@class.inner";
"['al']" = "@loop.outer";
"['il']" = "@loop.inner";
"['aa']" = "@call.outer";
"['ia']" = "@call.inner";
"['ar']" = "@parameter.outer";
"['ir']" = "@parameter.inner";
"['aC']" = "@comment.outer";
"['iC']" = "@comment.outer";
"['a/']" = "@comment.outer";
"['i/']" = "@comment.outer";
"['a;']" = "@statement.outer";
"['i;']" = "@statement.outer";
};
};
};
};
# Use mkAfter to ensure tree-sitter is already loaded
lua = lib.mkAfter ''
-- Use HCL parser with .tf files
vim.treesitter.language.register('hcl', 'terraform')
'';
}

View File

@ -1,70 +0,0 @@
local telescope = require("telescope.builtin")
vim.keymap.set("n", "<Leader>k", telescope.keymaps)
vim.keymap.set("n", "<Leader>/", telescope.live_grep)
vim.keymap.set("n", "<Leader>ff", telescope.find_files)
vim.keymap.set("n", "<Leader>fp", telescope.git_files)
vim.keymap.set("n", "<Leader>fw", telescope.grep_string)
vim.keymap.set("n", "<Leader>b", telescope.buffers)
vim.keymap.set("n", "<Leader>hh", telescope.help_tags)
vim.keymap.set("n", "<Leader>fr", telescope.oldfiles)
vim.keymap.set("n", "<Leader>cc", telescope.commands)
vim.keymap.set("n", "<Leader>gc", telescope.git_commits)
vim.keymap.set("n", "<Leader>gf", telescope.git_bcommits)
vim.keymap.set("n", "<Leader>gb", telescope.git_branches)
vim.keymap.set("n", "<Leader>gs", telescope.git_status)
vim.keymap.set("n", "<Leader>s", telescope.current_buffer_fuzzy_find)
vim.keymap.set("n", "<Leader>rr", telescope.resume)
vim.keymap.set("n", "<Leader>N", function()
local opts = {
prompt_title = "Search Notes",
cwd = "$NOTES_PATH",
}
telescope.live_grep(opts)
end)
vim.keymap.set("n", "<Leader>fN", function()
local opts = {
prompt_title = "Find Notes",
cwd = "$NOTES_PATH",
}
telescope.find_files(opts)
end)
vim.keymap.set("n", "<Leader>cr", function()
local opts = require("telescope.themes").get_ivy({
layout_config = {
bottom_pane = {
height = 15,
},
},
})
telescope.command_history(opts)
end)
-- Zoxide
vim.keymap.set("n", "<Leader>fz", require("telescope").extensions.zoxide.list)
-- Project
require("telescope").load_extension("projects")
vim.keymap.set("n", "<C-p>", function()
local opts = require("telescope.themes").get_ivy({
layout_config = {
bottom_pane = {
height = 10,
},
},
})
require("telescope").extensions.projects.projects(opts)
end)
-- File browser
require("telescope").load_extension("file_browser")
vim.keymap.set("n", "<Leader>fa", require("telescope").extensions.file_browser.file_browser)
vim.keymap.set("n", "<Leader>fD", function()
local opts = {
prompt_title = "Find Downloads",
cwd = "~/downloads",
}
require("telescope").extensions.file_browser.file_browser(opts)
end)

View File

@ -1,43 +0,0 @@
{ pkgs, dsl, ... }:
{
# Telescope is a fuzzy finder that can work with different sub-plugins
plugins = [
pkgs.vimPlugins.telescope-nvim
pkgs.vimPlugins.project-nvim
pkgs.vimPlugins.telescope-fzy-native-nvim
pkgs.vimPlugins.telescope-file-browser-nvim
pkgs.vimPlugins.telescope-zoxide
];
setup.telescope = {
defaults = {
mappings = {
i = {
"['<esc>']" = dsl.rawLua "require('telescope.actions').close";
"['<C-h>']" = "which_key";
};
};
};
pickers = {
find_files = {
theme = "ivy";
};
oldfiles = {
theme = "ivy";
};
buffers = {
theme = "dropdown";
};
};
extensions = {
fzy_native = { };
zoxide = { };
};
};
setup.project_nvim = { };
lua = builtins.readFile ./telescope.lua;
}

View File

@ -1,30 +0,0 @@
vim.keymap.set("t", "<A-CR>", "<C-\\><C-n>") --- Exit terminal mode
-- Only set these keymaps for toggleterm
vim.api.nvim_create_autocmd("TermOpen", {
pattern = "term://*toggleterm#*",
callback = function()
-- vim.keymap.set("t", "<Esc>", "<C-\\><C-n>") --- Exit terminal mode
vim.keymap.set("t", "<C-h>", "<C-\\><C-n><C-w>h")
vim.keymap.set("t", "<C-j>", "<C-\\><C-n><C-w>j")
vim.keymap.set("t", "<C-k>", "<C-\\><C-n><C-w>k")
vim.keymap.set("t", "<C-l>", "<C-\\><C-n><C-w>l")
end,
})
-- These are all the different types of terminals we can trigger
local terminal = require("toggleterm.terminal").Terminal
local basicterminal = terminal:new()
function TERM_TOGGLE()
basicterminal:toggle()
end
local nixpkgs = terminal:new({ cmd = "nix repl --expr 'import <nixpkgs>{}'" })
function NIXPKGS_TOGGLE()
nixpkgs:toggle()
end
vim.keymap.set("n", "<Leader>t", TERM_TOGGLE)
vim.keymap.set("n", "<Leader>P", NIXPKGS_TOGGLE)

View File

@ -1,24 +0,0 @@
{
pkgs,
dsl,
config,
...
}:
{
# Toggleterm provides a floating terminal inside the editor for quick access
plugins = [ pkgs.vimPlugins.toggleterm-nvim ];
use.toggleterm.setup = dsl.callWith {
open_mapping = dsl.rawLua "[[<c-\\>]]";
hide_numbers = true;
direction = "float";
};
lua = ''
${builtins.readFile ./toggleterm.lua}
${if config.github then (builtins.readFile ./github.lua) else ""}
${if config.kubernetes then (builtins.readFile ./kubernetes.lua) else ""}
'';
}

View File

@ -1,84 +0,0 @@
{ pkgs, dsl, ... }:
{
# This plugin creates a side drawer for navigating the current project
plugins = [
pkgs.vimPlugins.nvim-tree-lua
pkgs.vimPlugins.nvim-web-devicons
];
# Disable netrw eagerly
# https://github.com/kyazdani42/nvim-tree.lua/commit/fb8735e96cecf004fbefb086ce85371d003c5129
vim.g = {
loaded = 1;
loaded_netrwPlugin = 1;
};
setup.nvim-tree = {
disable_netrw = true; # Disable the built-in file manager
hijack_netrw = true; # Works as the file manager
sync_root_with_cwd = true; # Change project whenever currend dir changes
respect_buf_cwd = true; # Change to exact location of focused buffer
update_focused_file = {
# Change project based on the focused buffer
enable = true;
update_root = true;
ignore_list = { };
};
diagnostics = {
# Enable LSP and linter integration
enable = true;
icons = {
hint = "";
info = "";
warning = "";
error = "";
};
};
renderer = {
# Show files with changes vs. current commit
icons = {
glyphs = {
git = {
unstaged = "~";
staged = "+";
unmerged = "";
renamed = "";
deleted = "";
untracked = "?";
ignored = "";
};
};
};
};
# Set keybinds and initialize program
on_attach = dsl.rawLua ''
function (bufnr)
local api = require('nvim-tree.api')
local function opts(desc)
return { desc = 'nvim-tree: ' .. desc, buffer = bufnr, noremap = true, silent = true, nowait = true }
end
api.config.mappings.default_on_attach(bufnr)
vim.keymap.set('n', 'l', api.node.open.edit, opts('Open'))
vim.keymap.set('n', '<CR>', api.node.open.edit, opts('Open'))
vim.keymap.set('n', 'o', api.node.open.edit, opts('Open'))
vim.keymap.set('n', 'h', api.node.navigate.parent_close, opts('Close Directory'))
vim.keymap.set('n', 'v', api.node.open.vertical, opts('Open: Vertical Split'))
end
'';
view = {
# Set look and feel
width = 30;
side = "left";
number = false;
relativenumber = false;
};
};
# Toggle the sidebar
lua = ''
vim.keymap.set("n", "<Leader>e", ":NvimTreeFindFileToggle<CR>", { silent = true })
'';
}

View File

@ -1,67 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
neovim = import ./package {
inherit pkgs;
colors = config.theme.colors;
terraform = config.terraform.enable;
github = true;
kubernetes = config.kubernetes.enable;
};
in
{
options.neovim.enable = lib.mkEnableOption "Neovim.";
config = lib.mkIf config.neovim.enable {
home-manager.users.${config.user} =
{
home.packages = [ neovim ];
# Use Neovim as the editor for git commit messages
programs.git.extraConfig.core.editor = "nvim";
programs.jujutsu.settings.ui.editor = "nvim";
# Set Neovim as the default app for text editing and manual pages
home.sessionVariables = {
EDITOR = "nvim";
MANPAGER = "nvim +Man!";
};
# Create quick aliases for launching Neovim
programs.fish = {
shellAliases = {
vim = "nvim";
};
shellAbbrs = {
v = lib.mkForce "nvim";
vl = lib.mkForce "nvim -c 'normal! `0' -c 'bdelete 1'";
vll = "nvim -c 'Telescope oldfiles'";
};
};
# Create a desktop option for launching Neovim from a file manager
# (Requires launching the terminal and then executing Neovim)
xdg.desktopEntries.nvim = lib.mkIf (pkgs.stdenv.isLinux && config.gui.enable) {
name = "Neovim wrapper";
exec = "${config.home-manager.users.${config.user}.programs.rofi.terminal} nvim %F";
mimeType = [
"text/plain"
"text/markdown"
];
};
xdg.mimeApps.defaultApplications = lib.mkIf pkgs.stdenv.isLinux {
"text/plain" = [ "nvim.desktop" ];
"text/markdown" = [ "nvim.desktop" ];
};
};
};
}

View File

@ -1,78 +0,0 @@
-- ===========================================================================
-- Key Mapping
-- ===========================================================================
-- Function to cut down config boilerplate
local key = function(mode, key_sequence, action, params)
params = params or {}
vim.keymap.set(mode, key_sequence, action, params)
end
-- Remap space as leader key
key("", "<Space>", "<Nop>", { silent = true })
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- Keep selection when changing indentation
key("v", "<", "<gv")
key("v", ">", ">gv")
-- Clear search register
key("n", "<CR>", ":noh<CR><CR>", { silent = true })
-- Shuffle lines around
key("n", "<A-j>", ":m .+1<CR>==")
key("n", "<A-k>", ":m .-2<CR>==")
key("v", "<A-j>", ":m '>+1<CR>gv=gv")
key("v", "<A-k>", ":m '<-2<CR>gv=gv")
-- Better window navigation
key("n", "<C-h>", "<C-w>h")
key("n", "<C-j>", "<C-w>j")
key("n", "<C-k>", "<C-w>k")
key("n", "<C-l>", "<C-w>l")
-- File commands
key("n", "<Leader>q", ":quit<CR>")
key("n", "<Leader>Q", ":quitall<CR>")
key("n", "<Leader>fs", ":write<CR>")
key("n", "<Leader>fd", ":lcd %:p:h<CR>", { silent = true })
key("n", "<Leader>fu", ":lcd ..<CR>", { silent = true })
key("n", "<Leader><Tab>", ":b#<CR>", { silent = true })
key("n", "<Leader>tt", [[<Cmd>exe 'edit $NOTES_PATH/journal/'.strftime("%Y-%m-%d_%a").'.md'<CR>]])
key("n", "<Leader>jj", ":!journal<CR>:e<CR>")
-- Window commands
key("n", "<Leader>wv", ":vsplit<CR>")
key("n", "<Leader>wh", ":split<CR>")
key("n", "<Leader>wm", ":only<CR>")
-- Keep cursor in place
key("n", "n", "nzz")
key("n", "N", "Nzz")
key("n", "J", "mzJ`z") --- Mark and jump back to it
-- Add undo breakpoints
key("i", ",", ",<C-g>u")
key("i", ".", ".<C-g>u")
key("i", "!", "!<C-g>u")
key("i", "?", "?<C-g>u")
-- Resize with arrows
key("n", "<C-Up>", ":resize +2<CR>", { silent = true })
key("n", "<C-Down>", ":resize -2<CR>", { silent = true })
key("n", "<C-Left>", ":vertical resize -2<CR>", { silent = true })
key("n", "<C-Right>", ":vertical resize +2<CR>", { silent = true })
-- Quickfix
key("n", "]q", ":cnext<CR>")
key("n", "[q", ":cprevious<CR>")
key("n", "co", ":copen<CR>")
key("n", "cq", ":cclose<CR>")
-- Other
key("n", "<A-CR>", ":noh<CR>", { silent = true }) --- Clear search in VimWiki
key("n", "Y", "y$") --- Copy to end of line
key("v", "<C-r>", "y<Esc>:%s/<C-r>+//gc<left><left><left>") --- Substitute selected
key("v", "D", "y'>gp") --- Duplicate selected
key("x", "<Leader>p", '"_dP') --- Paste but keep register

View File

@ -1,30 +0,0 @@
-- ===========================================================================
-- Settings
-- ===========================================================================
vim.filetype.add({
pattern = {
[".*%.tfvars"] = "terraform",
[".*%.tf"] = "terraform",
[".*%.rasi"] = "rasi",
},
})
vim.api.nvim_create_autocmd("FileType", {
pattern = "mail",
callback = function()
vim.o.wrapmargin = 79 -- Wrap text automatically
end,
})
vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
command = "TableModeEnable",
})
vim.api.nvim_create_autocmd("FileType", {
pattern = "terraform",
callback = function()
vim.bo.commentstring = "# %s"
end,
})

View File

@ -1,62 +0,0 @@
# { inputs, globals, extraConfig ? [ ], ... }:
#
# let
#
# pkgs = import inputs.nixpkgs {
# system = inputs.system;
# overlays = [
# (import ./modules/neovim/plugins-overlay.nix inputs)
# inputs.nix2vim.overlay
# ];
# };
#
# in pkgs.neovimBuilder {
# package = pkgs.neovim-unwrapped;
# imports = [
# ./modules/common/neovim/plugins/bufferline.nix
# ./modules/common/neovim/plugins/completion.nix
# ./modules/common/neovim/plugins/gitsigns.nix
# ./modules/common/neovim/plugins/lsp.nix
# ./modules/common/neovim/plugins/misc.nix
# ./modules/common/neovim/plugins/statusline.nix
# ./modules/common/neovim/plugins/syntax.nix
# ./modules/common/neovim/plugins/telescope.nix
# ./modules/common/neovim/plugins/toggleterm.nix
# ./modules/common/neovim/plugins/tree.nix
# ] ++ extraConfig;
# }
{
pkgs,
colors,
terraform ? false,
github ? false,
kubernetes ? false,
...
}:
# Comes from nix2vim overlay:
# https://github.com/gytis-ivaskevicius/nix2vim/blob/master/lib/neovim-builder.nix
pkgs.neovimBuilder {
package = pkgs.neovim-unwrapped;
inherit
colors
terraform
github
kubernetes
;
imports = [
../config/align.nix
../config/bufferline.nix
../config/colors.nix
../config/completion.nix
../config/gitsigns.nix
../config/lsp.nix
../config/misc.nix
../config/statusline.nix
../config/syntax.nix
../config/telescope.nix
../config/toggleterm.nix
../config/tree.nix
];
}

View File

@ -1,8 +0,0 @@
expression: (apply_expression
function: (apply_expression
function: (select_expression
attrpath: (attrpath) @writeshell (#match? @writeshell "^writeShell.*$")
)
)
(indented_string_expression) @bash
)

View File

@ -1,12 +0,0 @@
{ ... }:
{
imports = [
./haskell.nix
./kubernetes.nix
./lua.nix
./python.nix
./rust.nix
./terraform.nix
];
}

View File

@ -1,12 +0,0 @@
{ config, lib, ... }:
{
options.haskell.enable = lib.mkEnableOption "Haskell programming language.";
config = lib.mkIf config.haskell.enable {
# Binary Cache for Haskell.nix
nix.settings.trusted-public-keys = [ "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" ];
nix.settings.substituters = [ "https://cache.iog.io" ];
};
}

View File

@ -1,164 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.kubernetes.enable = lib.mkEnableOption "Kubernetes tools.";
config = lib.mkIf config.kubernetes.enable {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
kubectl # Basic Kubernetes queries
kubernetes-helm # Helm CLI
fluxcd # Bootstrap clusters with Flux
kustomize # Kustomize CLI (for Flux)
];
programs.fish.shellAbbrs = {
k = "kubectl";
pods = "kubectl get pods -A";
nodes = "kubectl get nodes";
deploys = "kubectl get deployments -A";
dash = "kube-dashboard";
ks = "k9s";
};
# Terminal Kubernetes UI
programs.k9s = {
enable = true;
settings = {
k9s = {
ui = {
enableMouse = true;
headless = true;
logoless = true;
crumbsless = false;
skin = "main";
};
};
};
skins = {
main = {
k9s = {
body = {
fgColor = config.theme.colors.base06;
bgColor = "default";
logoColor = config.theme.colors.base02; # *blue ?
};
# Search bar
prompt = {
fgColor = config.theme.colors.base06;
bgColor = "default";
suggestColor = config.theme.colors.base03;
};
# Header left side
info = {
fgColor = config.theme.colors.base04;
sectionColor = config.theme.colors.base05;
};
dialog = {
fgColor = config.theme.colors.base06;
bgColor = "default";
buttonFgColor = config.theme.colors.base06;
buttonBgColor = config.theme.colors.base0E;
buttonFocusFgColor = config.theme.colors.base07;
buttonFocusBgColor = config.theme.colors.base02; # *cyan
labelFgColor = config.theme.colors.base09;
fieldFgColor = config.theme.colors.base06;
};
frame = {
border = {
fgColor = config.theme.colors.base01;
focusColor = config.theme.colors.base06;
};
menu = {
fgColor = config.theme.colors.base06;
keyColor = config.theme.colors.base0E; # *magenta
numKeyColor = config.theme.colors.base0E; # *magenta
};
crumbs = {
fgColor = config.theme.colors.base06;
bgColor = config.theme.colors.base01;
activeColor = config.theme.colors.base03;
};
status = {
newColor = config.theme.colors.base04; # *cyan
modifyColor = config.theme.colors.base0D; # *blue
addColor = config.theme.colors.base0B; # *green
errorColor = config.theme.colors.base08; # *red
highlightColor = config.theme.colors.base09; # *orange
killColor = config.theme.colors.base03; # *comment
completedColor = config.theme.colors.base03; # *comment
};
title = {
fgColor = config.theme.colors.base06;
bgColor = "default";
highlightColor = config.theme.colors.base09; # *orange
counterColor = config.theme.colors.base0D; # *blue
filterColor = config.theme.colors.base0E; # *magenta
};
};
views = {
charts = {
bgColor = "default";
defaultDialColors = [
config.theme.colors.base0D
config.theme.colors.base08
];
# - *blue
# - *red
defaultChartColors = [
config.theme.colors.base0D
config.theme.colors.base08
];
# - *blue
# - *red
};
table = {
# List of resources
fgColor = config.theme.colors.base06;
bgColor = "default";
# Row selection
cursorFgColor = config.theme.colors.base07;
cursorBgColor = config.theme.colors.base01;
# Header row
header = {
fgColor = config.theme.colors.base0D;
bgColor = "default";
sorterColor = config.theme.colors.base0A; # *selection
};
};
xray = {
fgColor = config.theme.colors.base06;
bgColor = "default";
cursorColor = config.theme.colors.base06;
graphicColor = config.theme.colors.base0D;
showIcons = false;
};
yaml = {
keyColor = config.theme.colors.base0D;
colonColor = config.theme.colors.base04;
fgColor = config.theme.colors.base03;
};
logs = {
fgColor = config.theme.colors.base06;
bgColor = "default";
indicator = {
fgColor = config.theme.colors.base06;
bgColor = "default";
};
};
};
};
};
};
};
};
};
}

View File

@ -1,17 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.lua.enable = lib.mkEnableOption "Lua programming language.";
config = lib.mkIf config.lua.enable {
home-manager.users.${config.user}.home.packages = with pkgs; [
stylua # Lua formatter
sumneko-lua-language-server # Lua LSP
];
};
}

View File

@ -1,27 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.python.enable = lib.mkEnableOption "Python programming language.";
config = lib.mkIf config.python.enable {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
# python310 # Standard Python interpreter
pyright # Python language server
black # Python formatter
python310Packages.flake8 # Python linter
];
programs.fish.shellAbbrs = {
py = "python3";
};
};
};
}

View File

@ -1,27 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.rust.enable = lib.mkEnableOption "Rust programming language.";
config = lib.mkIf config.rust.enable {
home-manager.users.${config.user} = {
programs.fish.shellAbbrs = {
ca = "cargo";
};
home.packages = with pkgs; [
cargo
rustc
clippy
gcc
];
};
};
}

View File

@ -1,26 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
options.terraform.enable = lib.mkEnableOption "Terraform tools.";
config = lib.mkIf config.terraform.enable {
unfreePackages = [ "terraform" ];
home-manager.users.${config.user} = {
programs.fish.shellAbbrs = {
# Terraform
te = "terraform";
};
home.packages = with pkgs; [
terraform # Terraform executable
terraform-ls # Language server
tflint # Linter
];
};
};
}

View File

@ -1,8 +0,0 @@
{ ... }:
{
imports = [
./dotfiles.nix
./notes.nix
];
}

View File

@ -1,33 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
# Allows me to make sure I can work on my dotfiles locally
options.dotfiles.enable = lib.mkEnableOption "Clone dotfiles.";
config = lib.mkIf config.dotfiles.enable {
home-manager.users.${config.user} = {
home.activation = {
# Always clone dotfiles repository if it doesn't exist
cloneDotfiles = config.home-manager.users.${config.user}.lib.dag.entryAfter [ "writeBoundary" ] ''
if [ ! -d "${config.dotfilesPath}" ]; then
$DRY_RUN_CMD mkdir --parents $VERBOSE_ARG $(dirname "${config.dotfilesPath}")
$DRY_RUN_CMD ${pkgs.git}/bin/git \
clone ${config.dotfilesRepo} "${config.dotfilesPath}"
fi
'';
};
# Set a variable for dotfiles repo, not necessary but convenient
home.sessionVariables.DOTS = config.dotfilesPath;
};
};
}

View File

@ -1,36 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
# This is just a placeholder as I expect to interact with my notes in a
# certain location
home-manager.users.${config.user} = {
home.sessionVariables = {
NOTES_PATH = "${config.homePath}/dev/personal/notes/content";
};
# Sync notes for Nextcloud automatically
systemd.user.timers.refresh-notes = lib.mkIf config.services.nextcloud.enable {
Timer = {
OnCalendar = "*-*-* *:0/10:50"; # Every 10 minutes
Unit = "refresh-notes.service";
};
};
systemd.user.services.refresh-notes = {
Unit.Description = "Get latest notes.";
Service = {
Type = "oneshot";
ExecStartPre = "${pkgs.git}/bin/git -C /data/git/notes reset --hard master";
ExecStart = "${pkgs.git}/bin/git -C /data/git/notes pull";
WorkingDirectory = config.homePath;
Environment = "PATH=${pkgs.openssh}/bin";
};
};
};
}

View File

@ -1,39 +0,0 @@
{ config, lib, ... }:
{
# Shell history sync
options.atuin.enable = lib.mkEnableOption "Atuin";
config = {
home-manager.users.${config.user} = lib.mkIf config.atuin.enable {
programs.atuin = {
enable = true;
flags = [
"--disable-up-arrow"
"--disable-ctrl-r"
];
settings = {
auto_sync = true;
update_check = false;
sync_address = "https://api.atuin.sh";
search_mode = "fuzzy";
filter_mode = "host"; # global, host, session, directory
search_mode_shell_up_key_binding = "fuzzy";
filter_mode_shell_up_key_binding = "session";
style = "compact"; # or auto,full
show_help = true;
history_filter = [ ];
secrets_filter = true;
enter_accept = false;
keymap_mode = "vim-normal";
};
};
};
# Give root user the same setup
home-manager.users.root.programs.atuin = config.home-manager.users.${config.user}.programs.atuin;
};
}

View File

@ -1,24 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
config = {
home-manager.users.${config.user} = {
programs.bash = {
enable = true;
shellAliases = config.home-manager.users.${config.user}.programs.fish.shellAliases;
initExtra = "";
profileExtra = "";
};
programs.starship.enableBashIntegration = false;
programs.zoxide.enableBashIntegration = true;
programs.fzf.enableBashIntegration = true;
};
};
}

View File

@ -1,22 +0,0 @@
#!/usr/bin/env bash
# Retrieve list of AWS instances
# Use enter to jump into their sessions with SSM
# Specify AWS_PROFILE and AWS_REGION before running this script
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" |
jq -r \
'.Reservations[]
| .Instances[]
| .InstanceId + " - " +
(.PrivateIpAddress // "n/a") + " - " +
(.PublicIpAddress // "n/a") + " - " +
(.Tags // [] | from_entries | .Name // "n/a")' |
fzf \
--height 100% \
--layout reverse \
--header $'Press Enter to start SSM session\nInstance ID - Private IP - Public IP - Name' \
--preview "aws ec2 describe-instances --instance-ids \"\$(echo {} | cut -d' ' -f1)\" | jq -r '.Reservations[].Instances[0]'" \
--bind "enter:become(aws ssm start-session --target \$(echo {} | cut -d' ' -f1))"

View File

@ -1,26 +0,0 @@
#!/bin/sh
# Stop all containers
if [ "$(docker ps -a -q)" ]; then
echo "Stopping docker containers..."
docker stop "$(docker ps -a -q)"
else
echo "No running docker containers."
fi
# Remove all stopped containers
if [ "$(docker ps -a -q)" ]; then
echo "Removing docker containers..."
docker rm "$(docker ps -a -q)"
else
echo "No stopped docker containers."
fi
# Remove all untagged images
if docker images | grep -q "^<none>"; then
docker rmi "$(docker images | grep "^<none>" | awk '{print $3}')"
else
echo "No untagged docker images."
fi
echo "Cleaned up docker."

View File

@ -1,23 +0,0 @@
#!/usr/bin/env bash
# Adapted from: https://gist.github.com/reegnz/b9e40993d410b75c2d866441add2cb55
if [[ -z $1 ]] || [[ $1 == "-" ]]; then
input=$(mktemp)
trap 'rm -f $input' EXIT
cat /dev/stdin >"$input"
else
input=$1
fi
echo '' |
fzf --phony \
--height 100% \
--preview-window='up:80%' \
--query '.' \
--print-query \
--header $'CTRL-O: jq output\nCTRL-Y: copy output\nALT-Y: copy query' \
--preview "jq --color-output -r {q} $input" \
--bind "ctrl-o:execute(jq -r {q} $input)+clear-query+accept" \
--bind "alt-y:execute(echo {q} | pbcopy)" \
--bind "ctrl-y:execute(jq -r {q} $input | pbcopy)"

View File

@ -1,59 +0,0 @@
#!/usr/bin/env bash
# Yoinked from https://github.com/JJGO/dotfiles
# Adapted from https://github.com/sdushantha/bin
set -x
TEXT_FILE="/tmp/ocr.txt"
IMAGE_FILE="/tmp/ocr.png"
function notify-send() {
/usr/bin/osascript -e "display notification \"$2\" with title \"OCR\""
}
PATH="/usr/local/bin/:$PATH"
# Take screenshot by selecting the area
/usr/sbin/screencapture -i "$IMAGE_FILE"
# Get the exit code of the previous command.
# So in this case, it is the screenshot command. If it did not exit with an
# exit code 0, then it means the user canceled the process of taking a
# screenshot by doing something like pressing the escape key
STATUS=$?
# If the user pressed the escape key or did something to terminate the proccess
# taking a screenshot, then just exit
[ $STATUS -ne 0 ] && exit 1
# Do the magic (∩^o^)⊃━☆゚.*・。゚
# Notice how I have removing the extension .txt from the file path. This is
# because tesseract adds .txt to the given file path anyways. So if we were to
# specify /tmp/ocr.txt as the file path, tesseract would out the text to
# /tmp/ocr.txt.txt
cd /tmp || {
echo "Failed to jump to directory."
exit 1
}
tesseract "$IMAGE_FILE" "${TEXT_FILE//\.txt/}"
# Check if the text was detected by checking number
# of lines in the file
LINES=$(wc -l <$TEXT_FILE)
if [ "$LINES" -eq 0 ]; then
notify-send "ocr" "no text was detected"
exit 1
fi
# Copy text to clipboard
# xclip -selection clip < "$TEXT_FILE"
/usr/bin/pbcopy <"$TEXT_FILE"
# Send a notification with the text that was grabbed using OCR
notify-send "ocr" "$(cat $TEXT_FILE)"
# Clean up
# "Always leave the area better than you found it"
# - My first grade teacher
rm "$TEXT_FILE"
rm "$IMAGE_FILE"

View File

@ -1,55 +0,0 @@
#!/usr/bin/env bash
export AWS_PROFILE="gs"
BUCKET_NAME_PART_1="t2"
BUCKET_NAME_PART_2="global"
BUCKET_NAME_PART_3="terraformstate"
PROJECT_ROOT=$(git rev-parse --show-toplevel)
WORKFLOW_FILE="${PROJECT_ROOT}/.github/workflows/terraform.yml"
if [ ! -f "$WORKFLOW_FILE" ]; then
WORKFLOW_FILE="${PROJECT_ROOT}/.github/workflows/apply.yml"
fi
AWS_ACCOUNT_NUMBER=$(
awk '/aws_account_number: .*/ {print $2}' "$WORKFLOW_FILE" | # Grab account number
echo "$(
read -r s
s=${s//\'/}
echo "$s"
)" # Remove single quote if it exists
)
if [ -z "${AWS_ACCOUNT_NUMBER}" ]; then
AWS_ACCOUNT_NUMBER=$(
awk '/AWS_ACCOUNT_NUMBER: .*/ {print $2}' "$WORKFLOW_FILE" | # Grab account number
echo "$(
read -r s
s=${s//\'/}
echo "$s"
)" # Remove single quote if it exists
)
fi
REPOSITORY=$(
git remote get-url origin |
awk -F'/' -v OFS='/' '{print $(NF-1),$NF }' |
echo "$(
read -r s
s=${s%.git}
echo "$s"
)" # Remove .git suffix if it exists
)
BRANCH=$(git branch --show-current)
terraform init \
-backend-config="region=us-east-1" \
-backend-config="bucket=${BUCKET_NAME_PART_1}${BUCKET_NAME_PART_2}${BUCKET_NAME_PART_3}" \
-backend-config="workspace_key_prefix=accounts/${AWS_ACCOUNT_NUMBER}/${REPOSITORY}" \
-backend-config="key=state.tfstate" \
-backend-config="dynamodb_table=global-tf-state-lock" \
-upgrade
terraform workspace select "$BRANCH"

View File

@ -1,22 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
# Convenience utilities from charm.sh
options.charm.enable = lib.mkEnableOption "Charm utilities.";
config.home-manager.users.${config.user} = lib.mkIf config.charm.enable {
home.packages = with pkgs; [
glow # Markdown previews
skate # Key-value store
charm # Manage account and filesystem
pop # Send emails from a TUI
];
};
}

View File

@ -1,18 +0,0 @@
{ ... }:
{
imports = [
./atuin.nix
./bash
./charm.nix
./direnv.nix
./fish
./fzf.nix
./git.nix
./github.nix
./jujutsu.nix
./nixpkgs.nix
./starship.nix
./utilities.nix
./work.nix
];
}

View File

@ -1,36 +0,0 @@
{ config, ... }:
{
# Enables quickly entering Nix shells when changing directories
home-manager.users.${config.user}.programs.direnv = {
enable = true;
nix-direnv.enable = true;
config = {
whitelist = {
prefix = [ config.dotfilesPath ];
};
};
};
# programs.direnv.direnvrcExtra = ''
# layout_postgres() {
# export PGDATA="$(direnv_layout_dir)/postgres"
# export PGHOST="$PGDATA"
#
# if [[ ! -d "PGDATA" ]]; then
# initdb
# cat >> "$PGDATA/postgres.conf" <<- EOF
# listen_addresses = '''
# unix_socket_directories = '$PGHOST'
# EOF
# echo "CREATE DATABASE $USER;" | postgres --single -E postgres
# fi
# }
# '';
# Prevent garbage collection
nix.extraOptions = ''
keep-outputs = true
keep-derivations = true
'';
}

View File

@ -1,154 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
users.users.${config.user}.shell = pkgs.fish;
programs.fish.enable = true; # Needed for LightDM to remember username
home-manager.users.${config.user} = {
# Packages used in abbreviations and aliases
home.packages = with pkgs; [ curl ];
programs.fish = {
enable = true;
shellAliases = {
# Version of bash which works much better on the terminal
bash = "${pkgs.bashInteractive}/bin/bash";
# Use eza (exa) instead of ls for fancier output
ls = "${pkgs.eza}/bin/eza --group";
# Move files to XDG trash on the commandline
trash = lib.mkIf pkgs.stdenv.isLinux "${pkgs.trash-cli}/bin/trash-put";
};
functions = {
commandline-git-commits = {
description = "Insert commit into commandline";
body = builtins.readFile ./functions/commandline-git-commits.fish;
};
copy = {
description = "Copy file contents into clipboard";
body = "cat $argv | pbcopy"; # Need to fix for non-macOS
};
edit = {
description = "Open a file in Vim";
body = builtins.readFile ./functions/edit.fish;
};
envs = {
description = "Evaluate a bash-like environment variables file";
body = ''set -gx (cat $argv | tr "=" " " | string split ' ')'';
};
fcd = {
description = "Jump to directory";
argumentNames = "directory";
body = builtins.readFile ./functions/fcd.fish;
};
fish_user_key_bindings = {
body = builtins.readFile ./functions/fish_user_key_bindings.fish;
};
ip = {
body = builtins.readFile ./functions/ip.fish;
};
json = {
description = "Tidy up JSON using jq";
body = "pbpaste | jq '.' | pbcopy"; # Need to fix for non-macOS
};
note = {
description = "Edit or create a note";
argumentNames = "filename";
body = builtins.readFile ./functions/note.fish;
};
recent = {
description = "Open a recent file in Vim";
body = builtins.readFile ./functions/recent.fish;
};
search-and-edit = {
description = "Search and open the relevant file in Vim";
body = builtins.readFile ./functions/search-and-edit.fish;
};
syncnotes = {
description = "Full git commit on notes";
body = builtins.readFile ./functions/syncnotes.fish;
};
_which = {
description = "Identify the path to a program in the shell";
body = "command --search (string sub --start=2 $argv)";
};
};
interactiveShellInit = ''
fish_vi_key_bindings
bind yy fish_clipboard_copy
bind Y fish_clipboard_copy
bind -M visual y fish_clipboard_copy
bind -M default p fish_clipboard_paste
set -g fish_vi_force_cursor
set -g fish_cursor_default block
set -g fish_cursor_insert line
set -g fish_cursor_visual block
set -g fish_cursor_replace_one underscore
'';
loginShellInit = "";
shellAbbrs = {
# Directory aliases
l = "ls -lh";
lh = "ls -lh";
ll = "ls -alhF";
la = "ls -a";
c = "cd";
"-" = "cd -";
mkd = "mkdir -pv";
# Convert a program into its full path
"=" = {
position = "anywhere";
regex = "=\\w+";
function = "_which";
};
# System
s = "sudo";
sc = "systemctl";
scs = "systemctl status";
sca = "systemctl cat";
m = "make";
t = "trash";
# Vim (overwritten by Neovim)
v = "vim";
vl = "vim -c 'normal! `0'";
# Notes
sn = "syncnotes";
# Fun CLI Tools
weather = "curl wttr.in/$WEATHER_CITY";
moon = "curl wttr.in/Moon";
# Cheat Sheets
ssl = "openssl req -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr";
fingerprint = "ssh-keyscan myhost.com | ssh-keygen -lf -";
publickey = "ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub";
forloop = "for i in (seq 1 100)";
# Docker
dc = "$DOTS/bin/docker_cleanup";
dr = "docker run --rm -it";
db = "docker build . -t";
};
shellInit = "";
};
home.sessionVariables.fish_greeting = "";
programs.starship.enableFishIntegration = true;
programs.zoxide.enableFishIntegration = true;
programs.fzf.enableFishIntegration = true;
};
}

View File

@ -1,6 +0,0 @@
set commit (git-commits)
if [ $commit ]
commandline -i "$commit"
else
commandline -i HEAD
end

View File

@ -1,4 +0,0 @@
set vimfile (fzf)
and set vimfile (echo $vimfile | tr -d '\r')
and commandline -r "vim \"$vimfile\""
and commandline -f execute

View File

@ -1,10 +0,0 @@
if test -z $directory
set directory "$HOME"
end
if ! test -d $directory
echo "Directory not found: $directory"
return 1
end
set jump (fd -t d . $directory | fzf)
and cd $jump $argv
and commandline -f execute

View File

@ -1,22 +0,0 @@
bind -M insert \co edit
bind -M default \co edit
bind -M insert \cs search-and-edit
bind -M default \cs search-and-edit
bind -M insert \ca 'cd ~; and edit; and commandline -a "; cd -"; commandline -f execute'
bind -M default \ca 'cd ~; and edit; and commandline -a "; cd -"; commandline -f execute'
bind -M insert \ce recent
bind -M default \ce recent
bind -M default \cg commandline-git-commits
bind -M insert \cg 'commandline -i (git rev-parse --show-toplevel 2>/dev/null || echo ".")'
bind -M insert \cf fcd
bind -M default \cf fcd
bind -M insert \cp projects
bind -M default \cp projects
bind -M insert \x1F accept-autosuggestion
bind -M default \x1F accept-autosuggestion
bind -M insert \cn 'commandline -r "nix shell nixpkgs#"'
bind -M default \cn 'commandline -r "nix shell nixpkgs#"'
bind -M insert \x11F nix-fzf
bind -M default \x11F nix-fzf
bind -M insert \ch '_atuin_search --filter-mode global'
bind -M default \ch '_atuin_search --filter-mode global'

View File

@ -1,122 +0,0 @@
#!/usr/local/bin/fish
function fish_vi_cursor -d 'Set cursor shape for different vi modes'
# If we're not interactive, there is effectively no bind mode.
if not status is-interactive
return
end
# This is hard to test in expect, since the exact sequences depend on the environment.
# Instead disable it.
if set -q FISH_UNIT_TESTS_RUNNING
return
end
# If this variable is set, skip all checks
if not set -q fish_vi_force_cursor
# Emacs Makes All Cursors Suck
if set -q INSIDE_EMACS
return
end
# vte-based terms set $TERM = xterm*, but only gained support in 2015.
# From https://bugzilla.gnome.org/show_bug.cgi?id=720821, it appears it was version 0.40.0
if set -q VTE_VERSION
and test "$VTE_VERSION" -lt 4000 2>/dev/null
return
end
# Similarly, genuine XTerm can do it since v280.
if set -q XTERM_VERSION
and not test (string replace -r "XTerm\((\d+)\)" '$1' -- "$XTERM_VERSION") -ge 280 2>/dev/null
return
end
# We need one of these terms.
# It would be lovely if we could rely on terminfo, but:
# - The "Ss" entry isn't a thing in macOS' old and crusty terminfo
# - It is set for xterm, and everyone and their dog claims to be xterm
#
# So we just don't care about $TERM, unless it is one of the few terminals that actually have their own entry.
#
# Note: Previous versions also checked $TMUX, and made sure that then $TERM was screen* or tmux*.
# We don't care, since we *cannot* handle term-in-a-terms 100% correctly.
if not set -q KONSOLE_PROFILE_NAME
and not test -n "$KONSOLE_VERSION" -a "$KONSOLE_VERSION" -ge 200400 # konsole, but new.
and not set -q ITERM_PROFILE
and not set -q VTE_VERSION # which version is already checked above
and not set -q WT_PROFILE_ID
and not set -q XTERM_VERSION
and not string match -rq '^st(-.*)$' -- $TERM
and not string match -q 'xterm-kitty*' -- $TERM
and not string match -q 'rxvt*' -- $TERM
and not string match -q 'alacritty*' -- $TERM
return
end
# HACK: Explicitly disable on ITERM because of #3696, which is weirdness with multi-line prompts.
# --force-iterm is now deprecated; set $fish_vi_force_cursor instead
if contains -- $argv[1] --force-iterm
set -e argv[1]
else if set -q ITERM_PROFILE
return
end
end
set -l terminal $argv[1]
set -q terminal[1]
or set terminal auto
set -l function
switch "$terminal"
case auto
# Nowadays, konsole does not set $KONSOLE_PROFILE_NAME anymore,
# and it uses the xterm sequences.
if set -q KONSOLE_PROFILE_NAME
set function __fish_cursor_konsole
else if set -q ITERM_PROFILE
set function __fish_cursor_1337
else
set function __fish_cursor_xterm
end
case konsole
set function __fish_cursor_konsole
case xterm
set function __fish_cursor_xterm
end
set -l tmux_prefix
set -l tmux_postfix
if set -q TMUX
set tmux_prefix echo -ne "'\ePtmux;\e'"
set tmux_postfix echo -ne "'\e\\\\'"
end
set -q fish_cursor_unknown
or set -g fish_cursor_unknown block blink
echo "
function fish_vi_cursor_handle --on-variable fish_bind_mode --on-event fish_postexec --on-event fish_focus_in
set -l varname fish_cursor_\$fish_bind_mode
if not set -q \$varname
set varname fish_cursor_unknown
end
$tmux_prefix
$function \$\$varname
$tmux_postfix
end
" | source
echo "
function fish_vi_cursor_handle_preexec --on-event fish_preexec
set -l varname fish_cursor_default
if not set -q \$varname
set varname fish_cursor_unknown
end
$tmux_prefix
$function \$\$varname
$tmux_postfix
end
" | source
end

View File

@ -1,14 +0,0 @@
set gitfile (git status -s \
| fzf \
--height 50% \
-m \
--preview-window right:70% \
--layout reverse \
--preview 'set -l IFS; set gd (git diff --color=always (echo {} | awk \'{$1=$1};1\' | cut -d" " -f2)); if test "$gd"; echo "$gd"; else; bat --color=always (echo {} | awk \'{$1=$1};1\' | cut -d" " -f2); end')
and for gf in $gitfile
set gf (echo $gf \
| awk '{$1=$1};1' \
| cut -d' ' -f2 \
)
and git add $gf
end

View File

@ -1,8 +0,0 @@
set commitline (git log \
--pretty="format:%C(auto)%ar %h%d %s" \
| fzf \
--height 50% \
--preview 'git show --color=always (echo {} | cut -d" " -f4)' \
)
and set commit (echo $commitline | cut -d" " -f4)
and echo $commit

View File

@ -1,10 +0,0 @@
set -l current (git rev-parse --abbrev-ref HEAD | tr -d '\n')
set -l branch (git branch \
--format "%(refname:short)" \
| fzf \
--height 50% \
--header="On $current, $header" \
--preview-window right:70% \
--preview 'git log {} --color=always --pretty="format:%C(auto)%ar %h%d %s"' \
)
and echo $branch

View File

@ -1,14 +0,0 @@
if not count $argv >/dev/null
echo "Must provide filename."
return 1
end
set commitline ( git log \
--follow \
--pretty="format:%C(auto)%ar %h%d %s" \
-- ./$argv \
| fzf \
--height 100% \
--preview "git diff --color=always (echo {} | cut -d' ' -f4)^1..(echo {} | cut -d' ' -f4) -- ./$argv" \
)
and set commit (echo $commitline | cut -d" " -f4)
and echo $commit

View File

@ -1,6 +0,0 @@
set commitline (git log \
--pretty="format:%C(auto)%ar %h%d %s" \
| fzf \
)
and set commit (echo $commitline | cut -d" " -f4 )
and git show $commit

View File

@ -1,37 +0,0 @@
if contains f $argv
switch $argv[1]
case checkout
git-checkout-fuzzy
case add
git-add-fuzzy
case show
git-show-fuzzy
case merge
git-merge-fuzzy
case branch
if test "$argv[2]" = -d
git-delete-fuzzy
else if test "$argv[2]" = -D
git-force-delete-fuzzy
else
echo "Not a fuzzy option."
return 1
end
case reset
set commit (git-commits)
and if test "$argv[2]" = --hard
git reset --hard $commit
else
git reset $commit
end
case "*"
echo "No fuzzy option."
return 1
end
else
if count $argv >/dev/null
command git $argv
else
command git status -sb
end
end

View File

@ -1,5 +0,0 @@
if count $argv >/dev/null
curl ipinfo.io/$argv
else
curl checkip.amazonaws.com
end

View File

@ -1,8 +0,0 @@
if test -n "$filename"
vim $NOTES_PATH/$filename.md
else
set file (ls $NOTES_PATH | fzf)
if [ $status -eq 0 ]
vim $NOTES_PATH/$file
end
end

View File

@ -1,4 +0,0 @@
set vimfile (fd -t f --exec /usr/bin/stat -f "%m%t%N" | sort -nr | cut -f2 | fzf)
and set vimfile (echo $vimfile | tr -d '\r')
and commandline -r "vim $vimfile"
and commandline -f execute

View File

@ -1,21 +0,0 @@
set vimfile ( \
rg \
--color=always \
--line-number \
--no-heading \
--smart-case \
--iglob "!/Library/**" \
--iglob "!/System/**" \
--iglob "!Users/$HOME/Library/*" \
".*" \
| fzf --ansi \
--height "80%" \
--color "hl:-1:underline,hl+:-1:underline:reverse" \
--delimiter : \
--preview 'bat --color=always {1} --highlight-line {2}' \
--preview-window 'up,60%,border-bottom,+{2}+3/3,~3'
)
and set line_number (echo $vimfile | tr -d '\r' | cut -d':' -f2)
and set vimfile (echo $vimfile | tr -d '\r' | cut -d':' -f1)
and commandline -r "vim +$line_number $vimfile"
and commandline -f execute

View File

@ -1,7 +0,0 @@
set current_dir $PWD
cd $NOTES_PATH
git pull
git add -A
git commit -m autosync
git push
cd $current_dir

View File

@ -1,9 +0,0 @@
echo "Searching git repos..." >&2
find "$HOME/dev" -type d -name '.git' | while read dir
set fullPath (dirname "$dir")
set relativePath (echo "$fullPath" | cut -d'/' -f5-)
if test -n (echo (git -C "$fullPath" status -s))
echo "$relativePath"
git -C "$fullPath" status -s
end
end

View File

@ -1,56 +0,0 @@
{ config, pkgs, ... }:
{
# FZF is a fuzzy-finder for the terminal
home-manager.users.${config.user} = {
programs.fzf.enable = true;
programs.fish = {
functions = {
projects = {
description = "Jump to a project";
body = ''
set projdir ( \
fd \
--search-path $HOME/dev \
--type directory \
--exact-depth 2 \
| ${pkgs.proximity-sort}/bin/proximity-sort $PWD \
| sed 's/\\/$//' \
| fzf --tiebreak=index \
)
and cd $projdir
and commandline -f execute
'';
};
};
shellAbbrs = {
lsf = "ls -lh | fzf";
};
};
# Global fzf configuration
home.sessionVariables =
let
fzfCommand = "fd --type file";
in
{
FZF_DEFAULT_COMMAND = fzfCommand;
FZF_CTRL_T_COMMAND = fzfCommand;
FZF_DEFAULT_OPTS = "-m --height 50% --border";
};
home.packages = [
(pkgs.writeShellApplication {
name = "jqr";
runtimeInputs = [
pkgs.jq
pkgs.fzf
];
text = builtins.readFile ./bash/scripts/jqr.sh;
})
];
};
}

View File

@ -1,195 +0,0 @@
{
config,
pkgs,
lib,
...
}:
let
home-packages = config.home-manager.users.${config.user}.home.packages;
in
{
options = {
gitName = lib.mkOption {
type = lib.types.str;
description = "Name to use for git commits";
};
gitEmail = lib.mkOption {
type = lib.types.str;
description = "Email to use for git commits";
};
};
config = {
home-manager.users.root.programs.git = {
enable = true;
extraConfig.safe.directory = config.dotfilesPath;
};
home-manager.users.${config.user} = {
programs.git = {
enable = true;
userName = config.gitName;
userEmail = config.gitEmail;
extraConfig = {
core.pager = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight | less -F";
interactive.difffilter = "${pkgs.git}/share/git/contrib/diff-highlight/diff-highlight";
pager = {
branch = "false";
};
safe = {
directory = config.dotfilesPath;
};
pull = {
ff = "only";
};
push = {
autoSetupRemote = "true";
};
init = {
defaultBranch = "master";
};
rebase = {
autosquash = "true";
};
gpg = {
format = "ssh";
ssh.allowedSignersFile = "~/.config/git/allowed-signers";
};
# commit.gpgsign = true;
# tag.gpgsign = true;
};
ignores = [
".direnv/**"
"result"
];
includes = [
{
path = "~/.config/git/personal";
condition = "gitdir:~/dev/personal/";
}
];
};
# Personal git config
# TODO: fix with variables
xdg.configFile."git/personal".text = ''
[user]
name = "${config.fullName}"
email = "7386960+nmasur@users.noreply.github.com"
signingkey = ~/.ssh/id_ed25519
[commit]
gpgsign = true
[tag]
gpgsign = true
'';
xdg.configFile."git/allowed-signers".text = ''
7386960+nmasur@users.noreply.github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB+AbmjGEwITk5CK9y7+Rg27Fokgj9QEjgc9wST6MA3s
'';
programs.fish.shellAbbrs = {
g = "git";
gs = "git status";
gd = "git diff";
gds = "git diff --staged";
gdp = "git diff HEAD^";
ga = "git add";
gaa = "git add -A";
gac = "git commit -am";
gc = "git commit -m";
gca = "git commit --amend --no-edit";
gcae = "git commit --amend";
gu = "git pull";
gp = "git push";
gl = "git log --graph --decorate --oneline -20";
gll = "git log --graph --decorate --oneline";
gco = "git checkout";
gcom = ''git switch (git symbolic-ref refs/remotes/origin/HEAD | cut -d"/" -f4)'';
gcob = "git switch -c";
gb = "git branch";
gpd = "git push origin -d";
gbd = "git branch -d";
gbD = "git branch -D";
gdd = {
position = "anywhere";
setCursor = true;
expansion = "BRANCH=% git push origin -d $BRANCH and git branch -d $BRANCH";
};
gr = "git reset";
grh = "git reset --hard";
gm = "git merge";
gcp = "git cherry-pick";
cdg = "cd (git rev-parse --show-toplevel)";
};
# Required for fish commands
home.packages = with pkgs; [
fish
fzf
bat
];
programs.fish.functions =
lib.mkIf (builtins.elem pkgs.fzf home-packages && builtins.elem pkgs.bat home-packages)
{
git = {
body = builtins.readFile ./fish/functions/git.fish;
};
git-add-fuzzy = {
body = builtins.readFile ./fish/functions/git-add-fuzzy.fish;
};
git-fuzzy-branch = {
argumentNames = "header";
body = builtins.readFile ./fish/functions/git-fuzzy-branch.fish;
};
git-checkout-fuzzy = {
body = ''
set branch (git-fuzzy-branch "checkout branch...")
and git checkout $branch
'';
};
git-delete-fuzzy = {
body = ''
set branch (git-fuzzy-branch "delete branch...")
and git branch -d $branch
'';
};
git-force-delete-fuzzy = {
body = ''
set branch (git-fuzzy-branch "force delete branch...")
and git branch -D $branch
'';
};
git-delete-both = {
argumentNames = "branch";
body = ''
git push origin -d $branch
git branch -d $branch
'';
};
git-merge-fuzzy = {
body = ''
set branch (git-fuzzy-branch "merge from...")
and git merge $branch
'';
};
git-show-fuzzy = {
body = builtins.readFile ./fish/functions/git-show-fuzzy.fish;
};
git-commits = {
body = builtins.readFile ./fish/functions/git-commits.fish;
};
git-history = {
body = builtins.readFile ./fish/functions/git-history.fish;
};
uncommitted = {
description = "Find uncommitted git repos";
body = builtins.readFile ./fish/functions/uncommitted.fish;
};
};
};
};
}

View File

@ -1,89 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
unfreePackages = [ "gh-copilot" ];
home-manager.users.${config.user} = {
programs.gh = lib.mkIf config.home-manager.users.${config.user}.programs.git.enable {
enable = true;
gitCredentialHelper.enable = true;
settings.git_protocol = "https";
extensions = [
pkgs.gh-collaborators
pkgs.gh-dash
pkgs.gh-copilot
];
};
programs.fish = lib.mkIf config.home-manager.users.${config.user}.programs.gh.enable {
shellAbbrs = {
ghr = "gh repo view -w";
gha = "gh run list | head -1 | awk '{ print \\$\\(NF-2\\) }' | xargs gh run view";
grw = "gh run watch";
grf = "gh run view --log-failed";
grl = "gh run view --log";
ghpr = "gh pr create && sleep 3 && gh run watch";
# https://github.com/cli/cli/discussions/4067
prs = "gh search prs --state=open --review-requested=@me";
};
functions = {
repos = {
description = "Clone GitHub repositories";
argumentNames = "organization";
body = ''
set directory (gh-repos $organization)
and cd $directory
'';
};
};
};
home.packages = [
(pkgs.writeShellScriptBin "gh-repos" ''
case $1 in
t2) organization="take-two" ;;
d2c) organization="take-two-t2gp" ;;
t2gp) organization="take-two-t2gp" ;;
pd) organization="private-division" ;;
dots) organization="playdots" ;;
*) organization="nmasur" ;;
esac
selected=$(gh repo list "$organization" \
--limit 1000 \
--no-archived \
--json=name,description,isPrivate,updatedAt,primaryLanguage \
| jq -r '.[] | .name + "," + if .description == "" then "-" else .description |= gsub(","; " ") | .description end + "," + .updatedAt + "," + .primaryLanguage.name' \
| (echo "REPO,DESCRIPTION,UPDATED,LANGUAGE"; cat -) \
| column -s , -t \
| fzf \
--header-lines=1 \
--layout=reverse \
--height=100% \
--bind "ctrl-o:execute:gh repo view -w ''${organization}/{1}" \
--bind "shift-up:preview-half-page-up" \
--bind "shift-down:preview-half-page-down" \
--preview "GH_FORCE_TTY=49% gh repo view ''${organization}/{1} | glow -" \
--preview-window up
)
[ -n "''${selected}" ] && {
directory="$HOME/dev/work"
if [ $organization = "nmasur" ]; then directory="$HOME/dev/personal"; fi
repo=$(echo "''${selected}" | awk '{print $1}')
repo_full="''${organization}/''${repo}"
if [ ! -d "''${directory}/''${repo}" ]; then
gh repo clone "$repo_full" "''${directory}/''${repo}"
fi
echo "''${directory}/''${repo}"
}
'')
];
};
}

View File

@ -1,18 +0,0 @@
{ config, ... }:
{
config = {
home-manager.users.${config.user}.programs.jujutsu = {
enable = true;
# https://github.com/martinvonz/jj/blob/main/docs/config.md
settings = {
user = {
name = config.home-manager.users.${config.user}.programs.git.userName;
email = config.home-manager.users.${config.user}.programs.git.userEmail;
};
};
};
};
}

View File

@ -1,133 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
home-manager.users.${config.user} = {
programs.fish = {
shellAbbrs = {
n = "nix";
ns = "nix-shell -p";
nsf = "nix-shell --run fish -p";
nsr = "nix-shell-run";
nps = "nix repl --expr 'import <nixpkgs>{}'";
nixo = "man configuration.nix";
nixh = "man home-configuration.nix";
nr = {
function = "rebuild-nixos";
};
nro = {
function = "rebuild-nixos-offline";
};
hm = {
function = "rebuild-home";
};
};
functions = {
nix-shell-run = {
body = ''
set program $argv[1]
if test (count $argv) -ge 2
commandline -r "nix run nixpkgs#$program -- $argv[2..-1]"
else
commandline -r "nix run nixpkgs#$program"
end
commandline -f execute
'';
};
nix-fzf = {
body = ''
commandline -i (nix-instantiate --eval --json \
-E 'builtins.attrNames (import <nixpkgs> {})' \
| jq '.[]' -r | fzf)
commandline -f repaint
'';
};
rebuild-nixos = {
body = ''
git -C ${config.dotfilesPath} add --intent-to-add --all
echo "doas nixos-rebuild switch --flake ${config.dotfilesPath}#${config.networking.hostName}"
'';
};
rebuild-nixos-offline = {
body = ''
git -C ${config.dotfilesPath} add --intent-to-add --all
echo "doas nixos-rebuild switch --option substitute false --flake ${config.dotfilesPath}#${config.networking.hostName}"
'';
};
rebuild-home = {
body = ''
git -C ${config.dotfilesPath} add --intent-to-add --all
echo "${pkgs.home-manager}/bin/home-manager switch --flake ${config.dotfilesPath}#${config.networking.hostName}";
'';
};
};
};
# Provides "command-not-found" options
programs.nix-index = {
enable = true;
enableFishIntegration = true;
};
# Create nix-index if doesn't exist
home.activation.createNixIndex =
let
cacheDir = "${config.homePath}/.cache/nix-index";
in
lib.mkIf config.home-manager.users.${config.user}.programs.nix-index.enable (
config.home-manager.users.${config.user}.lib.dag.entryAfter [ "writeBoundary" ] ''
if [ ! -d ${cacheDir} ]; then
$DRY_RUN_CMD ${pkgs.nix-index}/bin/nix-index -f ${pkgs.path}
fi
''
);
# Set automatic generation cleanup for home-manager
nix.gc = {
automatic = config.nix.gc.automatic;
options = config.nix.gc.options;
};
};
nix = {
# Set channel to flake packages, used for nix-shell commands
nixPath = [ "nixpkgs=${pkgs.path}" ];
# For security, only allow specific users
settings.allowed-users = [
"@wheel"
config.user
];
# Enable features in Nix commands
extraOptions = ''
experimental-features = nix-command flakes
warn-dirty = false
'';
gc = {
automatic = true;
options = "--delete-older-than 10d";
};
settings = {
# Add community Cachix to binary cache
# Don't use with macOS because blocked by corporate firewall
builders-use-substitutes = true;
substituters = lib.mkIf (!pkgs.stdenv.isDarwin) [ "https://nix-community.cachix.org" ];
trusted-public-keys = lib.mkIf (!pkgs.stdenv.isDarwin) [
"nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
];
# Scans and hard links identical files in the store
# Not working with macOS: https://github.com/NixOS/nix/issues/7273
auto-optimise-store = lib.mkIf (!pkgs.stdenv.isDarwin) true;
};
};
}

View File

@ -1,72 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
home-manager.users.${config.user}.programs.starship = {
enable = true;
settings = {
add_newline = false; # Don't print new line at the start of the prompt
format = lib.concatStrings [
"$directory"
"$git_branch"
"$git_commit"
"$git_status"
"$hostname"
"$cmd_duration"
"$character"
];
right_format = "$nix_shell";
character = {
success_symbol = "[](bold green)";
error_symbol = "[](bold red)";
vicmd_symbol = "[](bold green)";
};
cmd_duration = {
min_time = 5000;
show_notifications = if pkgs.stdenv.isLinux then false else true;
min_time_to_notify = 30000;
format = "[$duration]($style) ";
};
directory = {
truncate_to_repo = true;
truncation_length = 100;
};
git_branch = {
format = "[$symbol$branch]($style)";
};
git_commit = {
format = "( @ [$hash]($style) )";
only_detached = false;
};
git_status = {
format = "([$all_status$ahead_behind]($style) )";
conflicted = "=";
ahead = "";
behind = "";
diverged = "";
untracked = "";
stashed = "";
modified = "";
staged = "+";
renamed = "»";
deleted = "";
style = "red";
};
hostname = {
ssh_only = true;
format = "on [$hostname](bold red) ";
};
nix_shell = {
format = "[$symbol $name]($style)";
symbol = "";
};
python = {
format = "[\${version}\\(\${virtualenv}\\)]($style)";
};
};
};
}

View File

@ -1,77 +0,0 @@
{ config, pkgs, ... }:
let
ignorePatterns = ''
!.env*
!.github/
!.gitignore
!*.tfvars
.terraform/
.target/
/Library/'';
in
{
config = {
home-manager.users.${config.user} = {
home.packages = with pkgs; [
age # Encryption
bc # Calculator
delta # Fancy diffs
difftastic # Other fancy diffs
dig # DNS lookup
fd # find
htop # Show system processes
killall # Force quit
inetutils # Includes telnet, whois
jless # JSON viewer
jo # JSON output
jq # JSON manipulation
lf # File viewer
osc # Clipboard over SSH
qrencode # Generate qr codes
rsync # Copy folders
# ren # Rename files
# rep # Replace text in files
ripgrep # grep
sd # sed
spacer # Output lines in terminal
tealdeer # Cheatsheets
tree # View directory hierarchy
vimv-rs # Batch rename files
unzip # Extract zips
dua # File sizes (du)
du-dust # Disk usage tree (ncdu)
duf # Basic disk information (df)
];
programs.zoxide.enable = true; # Shortcut jump command
home.file = {
".rgignore".text = ignorePatterns;
".digrc".text = "+noall +answer"; # Cleaner dig commands
};
xdg.configFile."fd/ignore".text = ignorePatterns;
programs.bat = {
enable = true; # cat replacement
config = {
theme = config.theme.colors.batTheme;
pager = "less -R"; # Don't auto-exit if one screen
};
};
programs.fish.functions = {
ping = {
description = "Improved ping";
argumentNames = "target";
body = "${pkgs.prettyping}/bin/prettyping --nolegend $target";
};
};
};
};
}

View File

@ -1,656 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
# MacOS-specific settings for Alacritty
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
programs.alacritty.settings = {
font.size = lib.mkForce 20.0;
shell.program = "${pkgs.fish}/bin/fish";
window.startup_mode = "SimpleFullScreen";
key_bindings = [
{
key = "F";
mods = "Super";
action = "ToggleSimpleFullscreen";
}
{
key = "F";
mods = "Control|Shift";
action = "SearchForward";
}
{
key = "L";
mods = "Super";
chars = "\\x1F";
}
# Some Mac key combinations need to be manually added
{
key = "Return";
mods = "Shift";
chars = "\\x1b[13;2u";
}
{
key = "Return";
mods = "Control";
chars = "\\x1b[13;5u";
}
{
key = "Return";
mods = "Control|Shift";
chars = "\\x1b[13;6u";
}
{
key = "N";
mods = "Command";
action = "SpawnNewInstance";
}
{
key = "PageUp";
action = "ScrollPageUp";
}
{
key = "PageUp";
mods = "Alt";
chars = "\\x1B[5~";
}
{
key = "PageUp";
mods = "Shift";
chars = "\\x1B[5~";
}
{
key = "PageDown";
action = "ScrollPageDown";
}
{
key = "PageDown";
mods = "Alt";
chars = "\\x1B[6~";
}
{
key = "PageDown";
mods = "Shift";
chars = "\\x1B[6~";
}
{
key = "Home";
action = "ScrollToTop";
}
{
key = "Home";
mods = "Alt";
chars = "\\x1BOH";
}
{
key = "Home";
mods = "Shift";
chars = "\\x1B[H";
}
{
key = "End";
action = "ScrollToBottom";
}
{
key = "End";
mods = "Alt";
chars = "\\x1BOF";
}
{
key = "End";
mods = "Shift";
chars = "\\x1B[F";
}
{
key = "Tab";
mods = "Alt";
chars = "\\x1B ";
}
{
key = "Tab";
mods = "Alt|Shift";
chars = "\\x1Bx1B[Z";
}
# {
# key = "Back";
# chars = "\\x7F";
# }
{
key = "Back";
mods = "Alt";
chars = "\\x1Bx7F";
}
{
key = "Back";
mods = "Alt|Shift";
chars = "\\x1Bx08";
}
{
key = "Space";
mods = "Control";
chars = "\\x00";
}
{
key = "Space";
mods = "Alt";
chars = "\\x20";
}
{
key = "Left";
mods = "Alt";
chars = "\\x1Bb";
}
{
key = "Left";
mods = "Alt|Shift";
chars = "\\x1Bx1B[D";
}
{
key = "Right";
mods = "Alt";
chars = "\\x1Bf";
}
{
key = "Right";
mods = "Alt|Shift";
chars = "\\x1Bx1B[C";
}
{
key = "Down";
mods = "Alt";
chars = "\\x1Bx1B[B";
}
{
key = "Down";
mods = "Alt|Shift";
chars = "\\x1Bx1B[B";
}
{
key = "Up";
mods = "Alt";
chars = "\\x1Bx1B[A";
}
{
key = "Up";
mods = "Alt|Shift";
chars = "\\x1Bx1B[A";
}
{
key = "A";
mods = "Alt";
chars = "\\x1Ba";
}
{
key = "A";
mods = "Alt|Shift";
chars = "\\x1BA";
}
{
key = "B";
mods = "Alt";
chars = "\\x1Bb";
}
{
key = "B";
mods = "Alt|Shift";
chars = "\\x1BB";
}
{
key = "C";
mods = "Alt";
chars = "\\x1Bc";
}
{
key = "C";
mods = "Alt|Shift";
chars = "\\x1BC";
}
{
key = "D";
mods = "Alt";
chars = "\\x1Bd";
}
{
key = "D";
mods = "Alt|Shift";
chars = "\\x1BD";
}
{
key = "E";
mods = "Alt";
chars = "\\x1Be";
}
{
key = "E";
mods = "Alt|Shift";
chars = "\\x1BE";
}
{
key = "F";
mods = "Alt";
chars = "\\x1Bf";
}
{
key = "F";
mods = "Alt|Shift";
chars = "\\x1BF";
}
{
key = "G";
mods = "Alt";
chars = "\\x1Bg";
}
{
key = "G";
mods = "Alt|Shift";
chars = "\\x1BG";
}
{
key = "H";
mods = "Alt";
chars = "\\x1Bh";
}
{
key = "H";
mods = "Alt|Shift";
chars = "\\x1BH";
}
{
key = "I";
mods = "Alt";
chars = "\\x1Bi";
}
{
key = "I";
mods = "Alt|Shift";
chars = "\\x1BI";
}
{
key = "J";
mods = "Alt";
chars = "\\x1Bj";
}
{
key = "J";
mods = "Alt|Shift";
chars = "\\x1BJ";
}
{
key = "K";
mods = "Alt";
chars = "\\x1Bk";
}
{
key = "K";
mods = "Alt|Shift";
chars = "\\x1BK";
}
{
key = "L";
mods = "Alt";
chars = "\\x1Bl";
}
{
key = "L";
mods = "Alt|Shift";
chars = "\\x1BL";
}
{
key = "M";
mods = "Alt";
chars = "\\x1Bm";
}
{
key = "M";
mods = "Alt|Shift";
chars = "\\x1BM";
}
{
key = "N";
mods = "Alt";
chars = "\\x1Bn";
}
{
key = "N";
mods = "Alt|Shift";
chars = "\\x1BN";
}
{
key = "O";
mods = "Alt";
chars = "\\x1Bo";
}
{
key = "O";
mods = "Alt|Shift";
chars = "\\x1BO";
}
{
key = "P";
mods = "Alt";
chars = "\\x1Bp";
}
{
key = "P";
mods = "Alt|Shift";
chars = "\\x1BP";
}
{
key = "Q";
mods = "Alt";
chars = "\\x1Bq";
}
{
key = "Q";
mods = "Alt|Shift";
chars = "\\x1BQ";
}
{
key = "R";
mods = "Alt";
chars = "\\x1Br";
}
{
key = "R";
mods = "Alt|Shift";
chars = "\\x1BR";
}
{
key = "S";
mods = "Alt";
chars = "\\x1Bs";
}
{
key = "S";
mods = "Alt|Shift";
chars = "\\x1BS";
}
{
key = "T";
mods = "Alt";
chars = "\\x1Bt";
}
{
key = "T";
mods = "Alt|Shift";
chars = "\\x1BT";
}
{
key = "U";
mods = "Alt";
chars = "\\x1Bu";
}
{
key = "U";
mods = "Alt|Shift";
chars = "\\x1BU";
}
{
key = "V";
mods = "Alt";
chars = "\\x1Bv";
}
{
key = "V";
mods = "Alt|Shift";
chars = "\\x1BV";
}
{
key = "W";
mods = "Alt";
chars = "\\x1Bw";
}
{
key = "W";
mods = "Alt|Shift";
chars = "\\x1BW";
}
{
key = "X";
mods = "Alt";
chars = "\\x1Bx";
}
{
key = "X";
mods = "Alt|Shift";
chars = "\\x1BX";
}
{
key = "Y";
mods = "Alt";
chars = "\\x1By";
}
{
key = "Y";
mods = "Alt|Shift";
chars = "\\x1BY";
}
{
key = "Z";
mods = "Alt";
chars = "\\x1Bz";
}
{
key = "Z";
mods = "Alt|Shift";
chars = "\\x1BZ";
}
{
key = "Key1";
mods = "Alt";
chars = "\\x1B1";
}
{
key = "Key1";
mods = "Alt|Shift";
chars = "\\x1B!";
}
{
key = "Key2";
mods = "Alt";
chars = "\\x1B2";
}
{
key = "Key2";
mods = "Alt|Shift";
chars = "\\x1B#";
}
{
key = "Key3";
mods = "Alt";
chars = "\\x1B3";
}
{
key = "Key3";
mods = "Alt|Shift";
chars = "\\x1B#";
}
{
key = "Key4";
mods = "Alt";
chars = "\\x1B4";
}
{
key = "Key4";
mods = "Alt|Shift";
chars = "\\x1B$";
}
{
key = "Key5";
mods = "Alt";
chars = "\\x1B5";
}
{
key = "Key5";
mods = "Alt|Shift";
chars = "\\x1B%";
}
{
key = "Key6";
mods = "Alt";
chars = "\\x1B6";
}
{
key = "Key6";
mods = "Alt|Shift";
chars = "\\x1B^";
}
{
key = "Key7";
mods = "Alt";
chars = "\\x1B7";
}
{
key = "Key7";
mods = "Alt|Shift";
chars = "\\x1B&";
}
{
key = "Key8";
mods = "Alt";
chars = "\\x1B8";
}
{
key = "Key8";
mods = "Alt|Shift";
chars = "\\x1B*";
}
{
key = "Key9";
mods = "Alt";
chars = "\\x1B9";
}
{
key = "Key9";
mods = "Alt|Shift";
chars = "\\x1B(";
}
{
key = "Key0";
mods = "Alt";
chars = "\\x1B0";
}
{
key = "Key0";
mods = "Alt|Shift";
chars = "\\x1B)";
}
{
key = "Minus";
mods = "Alt";
chars = "\\x1B-";
}
{
key = "Minus";
mods = "Alt|Shift";
chars = "\\x1B_";
}
{
key = "Equals";
mods = "Alt";
chars = "\\x1B=";
}
{
key = "Equals";
mods = "Alt|Shift";
chars = "\\x1B+";
}
{
key = "LBracket";
mods = "Alt";
chars = "\\x1B[";
}
{
key = "LBracket";
mods = "Alt|Shift";
chars = "\\x1B{";
}
{
key = "RBracket";
mods = "Alt";
chars = "\\x1B]";
}
{
key = "RBracket";
mods = "Alt|Shift";
chars = "\\x1B}";
}
{
key = "Backslash";
mods = "Alt";
chars = "\\x1B\\\\";
}
{
key = "Backslash";
mods = "Alt|Shift";
chars = "\\x1B|";
}
{
key = "Semicolon";
mods = "Alt";
chars = "\\x1B;";
}
{
key = "Semicolon";
mods = "Alt|Shift";
chars = "\\x1B =";
}
{
key = "Apostrophe";
mods = "Alt";
chars = "\\x1B'";
}
{
key = "Apostrophe";
mods = "Alt|Shift";
chars = ''\x1B"'';
}
{
key = "Comma";
mods = "Alt";
chars = "\\x1B;";
}
{
key = "Comma";
mods = "Alt|Shift";
chars = "\\x1B<";
}
{
key = "Period";
mods = "Alt";
chars = "\\x1B.";
}
{
key = "Period";
mods = "Alt|Shift";
chars = "\\x1B>";
}
{
key = "Slash";
mods = "Alt";
chars = "\\x1B/";
}
{
key = "Slash";
mods = "Alt|Shift";
chars = "\\x1B?";
}
{
key = "Grave";
mods = "Alt";
chars = "\\x1B`";
}
{
key = "Grave";
mods = "Alt|Shift";
chars = "\\x1B~";
}
];
};
};
}

View File

@ -1,18 +0,0 @@
{ ... }:
{
imports = [
./alacritty.nix
./fish.nix
./fonts.nix
./hammerspoon.nix
./homebrew.nix
./kitty.nix
./networking.nix
./nixpkgs.nix
./system.nix
./tmux.nix
./user.nix
./utilities.nix
];
}

View File

@ -1,10 +0,0 @@
{ pkgs, ... }:
{
config = {
# MacOS-specific settings for Fish
programs.fish.useBabelfish = true;
programs.fish.babelfishPackage = pkgs.babelfish;
};
}

View File

@ -1,22 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
home-manager.users.${config.user} = lib.mkIf pkgs.stdenv.isDarwin {
home.packages = with pkgs; [ nerd-fonts.victor-mono ];
programs.alacritty.settings = {
font.normal.family = "VictorMono";
};
programs.kitty.font = {
package = pkgs.nerd-fonts.victor-mono;
name = "VictorMono Nerd Font Mono";
};
};
}

View File

@ -1,43 +0,0 @@
{
config,
pkgs,
lib,
...
}:
{
# Hammerspoon - MacOS custom automation scripting
config = lib.mkIf pkgs.stdenv.isDarwin {
home-manager.users.${config.user} = {
xdg.configFile."hammerspoon/init.lua".source = ./hammerspoon/init.lua;
xdg.configFile."hammerspoon/Spoons/ControlEscape.spoon".source = ./hammerspoon/Spoons/ControlEscape.spoon;
xdg.configFile."hammerspoon/Spoons/DismissAlerts.spoon".source = ./hammerspoon/Spoons/DismissAlerts.spoon;
xdg.configFile."hammerspoon/Spoons/Launcher.spoon/init.lua".source = pkgs.substituteAll {
src = ./hammerspoon/Spoons/Launcher.spoon/init.lua;
firefox = "${pkgs.firefox-bin}/Applications/Firefox.app";
discord = "${pkgs.discord}/Applications/Discord.app";
wezterm = "${pkgs.wezterm}/Applications/WezTerm.app";
obsidian = "${pkgs.obsidian}/Applications/Obsidian.app";
slack = "${pkgs.slack}/Applications/Slack.app";
};
xdg.configFile."hammerspoon/Spoons/MoveWindow.spoon".source = ./hammerspoon/Spoons/MoveWindow.spoon;
home.activation.reloadHammerspoon =
config.home-manager.users.${config.user}.lib.dag.entryAfter [ "writeBoundary" ]
''
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.reload()"
$DRY_RUN_CMD sleep 1
$DRY_RUN_CMD /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c "hs.console.clearConsole()"
'';
};
homebrew.casks = [ "hammerspoon" ];
system.activationScripts.postUserActivation.text = ''
defaults write org.hammerspoon.Hammerspoon MJConfigFile "${config.homePath}/.config/hammerspoon/init.lua"
sudo killall Dock
'';
};
}

View File

@ -1 +0,0 @@
indent_type = "Spaces"

View File

@ -1,112 +0,0 @@
--- === ControlEscape ===
---
--- Adapted very loosely from https://github.com/jasonrudolph/ControlEscape.spoon
--- Removed timing/delay; always send Escape as well as Control
---
--- Make the `control` key more useful: If the `control` key is tapped, treat it
--- as the `escape` key. If the `control` key is held down and used in
--- combination with another key, then provide the normal `control` key
--- behavior.
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "ControlEscape"
obj.version = "0.1"
obj.author = "Jason Rudolph <jason@jasonrudolph.com>"
obj.homepage = "https://github.com/jasonrudolph/ControlEscape.spoon"
obj.license = "MIT - https://opensource.org/licenses/MIT"
function obj:init()
self.movements = 0
self.sendEscape = false
self.lastModifiers = {}
-- Create an eventtap to run each time the modifier keys change (i.e., each
-- time a key like control, shift, option, or command is pressed or released)
self.controlTap = hs.eventtap.new({ hs.eventtap.event.types.flagsChanged }, function(event)
local newModifiers = event:getFlags()
-- If this change to the modifier keys does not involve a *change* to the
-- up/down state of the `control` key (i.e., it was up before and it's
-- still up, or it was down before and it's still down), then don't take
-- any action.
if self.lastModifiers["ctrl"] == newModifiers["ctrl"] then
return false
end
-- Control was not down but is now
if not self.lastModifiers["ctrl"] then
-- Only prepare to send escape if no other modifier keys are in use
self.lastModifiers = newModifiers
if not self.lastModifiers["cmd"] and not self.lastModifiers["alt"] then
self.sendEscape = true
self.movements = 0
end
-- Control was down and is up, hasn't been blocked by another key, and
-- isn't above the movement threshold
elseif self.sendEscape == true and not newModifiers["ctrl"] and self.movements < 30 then
self.lastModifiers = newModifiers
-- Allow for shift-escape
if newModifiers["shift"] then
hs.eventtap.keyStroke({ "shift" }, "escape", 0)
else
hs.eventtap.keyStroke(newModifiers, "escape", 0)
end
self.sendEscape = false
self.movements = 0
self.numberOfCharacters = 0
-- Control was down and is up, but isn't ready to send escape
else
self.lastModifiers = newModifiers
end
end)
-- If any other key is pressed, don't send escape
self.asModifier = hs.eventtap.new({ hs.eventtap.event.types.keyDown }, function(_)
self.sendEscape = false
-- print("Don't sent escape")
end)
-- If mouse is moving significantly, don't send escape
self.scrolling = hs.eventtap.new({ hs.eventtap.event.types.gesture }, function(event)
local touches = event:getTouches()
local i, v = next(touches, nil)
while i do
if v["phase"] == "moved" then
-- Increment the movement counter
self.movements = self.movements + 1
end
i, v = next(touches, i) -- get next index
end
end)
end
--- ControlEscape:start()
--- Method
--- Start sending `escape` when `control` is pressed and released in isolation
function obj:start()
self.controlTap:start()
self.asModifier:start()
self.scrolling:start()
end
--- ControlEscape:stop()
--- Method
--- Stop sending `escape` when `control` is pressed and released in isolation
function obj:stop()
-- Stop monitoring keystrokes
self.controlTap:stop()
self.asModifier:stop()
self.scrolling:stop()
-- Reset state
self.sendEscape = false
self.lastModifiers = {}
end
return obj

View File

@ -1,21 +0,0 @@
# Credit: https://github.com/Ptujec/LaunchBar/blob/f7b5a0dba9919c2fec879513f68a044f78748539/Notifications/Dismiss%20all%20notifications.lbaction/Contents/Scripts/default.applescript
tell application "System Events"
try
set _groups to groups of UI element 1 of scroll area 1 of group 1 of window "Notification Center" of application process "NotificationCenter"
repeat with _group in _groups
set _actions to actions of _group
repeat with _action in _actions
if description of _action is in {"Schlie§en", "Alle entfernen", "Close", "Clear All"} then
perform _action
end if
end repeat
end repeat
end try
end tell

View File

@ -1,298 +0,0 @@
/* Credit: https://gist.github.com/lancethomps/a5ac103f334b171f70ce2ff983220b4f */
function run(input, parameters) {
const appNames = [];
const skipAppNames = [];
const verbose = true;
const scriptName = "close_notifications_applescript";
const CLEAR_ALL_ACTION = "Clear All";
const CLEAR_ALL_ACTION_TOP = "Clear";
const CLOSE_ACTION = "Close";
const notNull = (val) => {
return val !== null && val !== undefined;
};
const isNull = (val) => {
return !notNull(val);
};
const notNullOrEmpty = (val) => {
return notNull(val) && val.length > 0;
};
const isNullOrEmpty = (val) => {
return !notNullOrEmpty(val);
};
const isError = (maybeErr) => {
return notNull(maybeErr) && (maybeErr instanceof Error || maybeErr.message);
};
const systemVersion = () => {
return Application("Finder").version().split(".").map(val => parseInt(val));
};
const systemVersionGreaterThanOrEqualTo = (vers) => {
return systemVersion()[0] >= vers;
};
const isBigSurOrGreater = () => {
return systemVersionGreaterThanOrEqualTo(11);
};
const V11_OR_GREATER = isBigSurOrGreater();
const APP_NAME_MATCHER_ROLE = V11_OR_GREATER ? "AXStaticText" : "AXImage";
const hasAppNames = notNullOrEmpty(appNames);
const hasSkipAppNames = notNullOrEmpty(skipAppNames);
const hasAppNameFilters = hasAppNames || hasSkipAppNames;
const appNameForLog = hasAppNames ? ` [${appNames.join(",")}]` : "";
const logs = [];
const log = (message, ...optionalParams) => {
let message_with_prefix = `${new Date().toISOString().replace("Z", "").replace("T", " ")} [${scriptName}]${appNameForLog} ${message}`;
console.log(message_with_prefix, optionalParams);
logs.push(message_with_prefix);
};
const logError = (message, ...optionalParams) => {
if (isError(message)) {
let err = message;
message = `${err}${err.stack ? (" " + err.stack) : ""}`;
}
log(`ERROR ${message}`, optionalParams);
};
const logErrorVerbose = (message, ...optionalParams) => {
if (verbose) {
logError(message, optionalParams);
}
};
const logVerbose = (message) => {
if (verbose) {
log(message);
}
};
const getLogLines = () => {
return logs.join("\n");
};
const getSystemEvents = () => {
let systemEvents = Application("System Events");
systemEvents.includeStandardAdditions = true;
return systemEvents;
};
const getNotificationCenter = () => {
try {
return getSystemEvents().processes.byName("NotificationCenter");
} catch (err) {
logError("Could not get NotificationCenter");
throw err;
}
};
const getNotificationCenterGroups = (retryOnError = false) => {
try {
let notificationCenter = getNotificationCenter();
if (notificationCenter.windows.length <= 0) {
return [];
}
if (!V11_OR_GREATER) {
return notificationCenter.windows();
}
return notificationCenter.windows[0].uiElements[0].uiElements[0].uiElements();
} catch (err) {
logError("Could not get NotificationCenter groups");
if (retryOnError) {
logError(err);
log("Retrying getNotificationCenterGroups...");
return getNotificationCenterGroups(false);
} else {
throw err;
}
}
};
const isClearButton = (description, name) => {
return description === "button" && name === CLEAR_ALL_ACTION_TOP;
};
const matchesAnyAppNames = (value, checkValues) => {
if (isNullOrEmpty(checkValues)) {
return false;
}
let lowerAppName = value.toLowerCase();
for (let checkValue of checkValues) {
if (lowerAppName === checkValue.toLowerCase()) {
return true;
}
}
return false;
};
const matchesAppName = (role, value) => {
if (role !== APP_NAME_MATCHER_ROLE) {
return false;
}
if (hasAppNames) {
return matchesAnyAppNames(value, appNames);
}
return !matchesAnyAppNames(value, skipAppNames);
};
const notificationGroupMatches = (group) => {
try {
let description = group.description();
if (V11_OR_GREATER && isClearButton(description, group.name())) {
return true;
}
if (V11_OR_GREATER && description !== "group") {
return false;
}
if (!V11_OR_GREATER) {
let matchedAppName = !hasAppNameFilters;
if (!matchedAppName) {
for (let elem of group.uiElements()) {
if (matchesAppName(elem.role(), elem.description())) {
matchedAppName = true;
break;
}
}
}
if (matchedAppName) {
return notNull(findCloseActionV10(group, -1));
}
return false;
}
if (!hasAppNameFilters) {
return true;
}
let firstElem = group.uiElements[0];
return matchesAppName(firstElem.role(), firstElem.value());
} catch (err) {
logErrorVerbose(`Caught error while checking window, window is probably closed: ${err}`);
logErrorVerbose(err);
}
return false;
};
const findCloseActionV10 = (group, closedCount) => {
try {
for (let elem of group.uiElements()) {
if (elem.role() === "AXButton" && elem.title() === CLOSE_ACTION) {
return elem.actions["AXPress"];
}
}
} catch (err) {
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
logErrorVerbose(err);
return null;
}
log("No close action found for notification");
return null;
};
const findCloseAction = (group, closedCount) => {
try {
if (!V11_OR_GREATER) {
return findCloseActionV10(group, closedCount);
}
let checkForPress = isClearButton(group.description(), group.name());
let clearAllAction;
let closeAction;
for (let action of group.actions()) {
let description = action.description();
if (description === CLEAR_ALL_ACTION) {
clearAllAction = action;
break;
} else if (description === CLOSE_ACTION) {
closeAction = action;
} else if (checkForPress && description === "press") {
clearAllAction = action;
break;
}
}
if (notNull(clearAllAction)) {
return clearAllAction;
} else if (notNull(closeAction)) {
return closeAction;
}
} catch (err) {
logErrorVerbose(`(group_${closedCount}) Caught error while searching for close action, window is probably closed: ${err}`);
logErrorVerbose(err);
return null;
}
log("No close action found for notification");
return null;
};
const closeNextGroup = (groups, closedCount) => {
try {
for (let group of groups) {
if (notificationGroupMatches(group)) {
let closeAction = findCloseAction(group, closedCount);
if (notNull(closeAction)) {
try {
closeAction.perform();
return [true, 1];
} catch (err) {
logErrorVerbose(`(group_${closedCount}) Caught error while performing close action, window is probably closed: ${err}`);
logErrorVerbose(err);
}
}
return [true, 0];
}
}
return false;
} catch (err) {
logError("Could not run closeNextGroup");
throw err;
}
};
try {
let groupsCount = getNotificationCenterGroups(true).filter(group => notificationGroupMatches(group)).length;
if (groupsCount > 0) {
logVerbose(`Closing ${groupsCount}${appNameForLog} notification group${(groupsCount > 1 ? "s" : "")}`);
let startTime = new Date().getTime();
let closedCount = 0;
let maybeMore = true;
let maxAttempts = 2;
let attempts = 1;
while (maybeMore && ((new Date().getTime() - startTime) <= (1000 * 30))) {
try {
let closeResult = closeNextGroup(getNotificationCenterGroups(), closedCount);
maybeMore = closeResult[0];
if (maybeMore) {
closedCount = closedCount + closeResult[1];
}
} catch (innerErr) {
if (maybeMore && closedCount === 0 && attempts < maxAttempts) {
log(`Caught an error before anything closed, trying ${maxAttempts - attempts} more time(s).`)
attempts++;
} else {
throw innerErr;
}
}
}
} else {
throw Error(`No${appNameForLog} notifications found...`);
}
} catch (err) {
logError(err);
logError(err.message);
getLogLines();
throw err;
}
return getLogLines();
}

View File

@ -1,17 +0,0 @@
--- === Dismiss Alerts ===
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "DismissAlerts"
obj.version = "0.1"
obj.license = "MIT - https://opensource.org/licenses/MIT"
function obj:init()
hs.hotkey.bind({ "cmd", "alt", "ctrl" }, "k", function()
hs.osascript.applescriptFromFile("Spoons/DismissAlerts.spoon/close_notifications.applescript")
end)
end
return obj

View File

@ -1,111 +0,0 @@
--- === Launcher ===
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "Launcher"
obj.version = "0.1"
obj.license = "MIT - https://opensource.org/licenses/MIT"
local screen = hs.screen.primaryScreen()
local switcherWidth = 500
function obj:init()
-- Begin launcher mode
if self.launcher == nil then
self.launcher = hs.hotkey.modal.new("ctrl", "space")
print(self.canvas)
print(obj.canvas)
end
-- Behaviors on enter
function self.launcher:entered()
-- hs.alert("Entered mode")
obj.canvas = hs.canvas.new({
x = (screen:fullFrame().x + screen:fullFrame().w) / 2 - switcherWidth / 2,
y = 1,
h = 3,
w = switcherWidth,
})
-- Draw switcher
obj.canvas[#obj.canvas + 1] = {
action = "build",
type = "rectangle",
}
obj.canvas[#obj.canvas + 1] = {
type = "rectangle",
fillColor = { alpha = 1, red = 0.8, green = 0.6, blue = 0.3 },
action = "fill",
}
obj.canvas:show()
end
-- Behaviors on exit
function self.launcher:exited()
-- hs.alert("Exited mode")
obj.canvas:delete(0.2)
end
-- Use escape to exit launcher mode
self.launcher:bind("", "escape", function()
self.launcher:exit()
end)
-- Launcher shortcuts
self.launcher:bind("ctrl", "space", function() end)
self.launcher:bind("", "return", function()
self:switch("@wezterm@")
end)
self.launcher:bind("", "C", function()
self:switch("Calendar.app")
end)
self.launcher:bind("shift", "D", function()
hs.execute("launchctl remove com.paloaltonetworks.gp.pangps")
hs.execute("launchctl remove com.paloaltonetworks.gp.pangpa")
hs.alert.show("Disconnected from GlobalProtect", nil, nil, 4)
self.launcher:exit()
end)
self.launcher:bind("", "E", function()
self:switch("Mail.app")
end)
self.launcher:bind("", "F", function()
self:switch("@firefox@")
end)
self.launcher:bind("", "H", function()
self:switch("Hammerspoon.app")
end)
self.launcher:bind("", "M", function()
self:switch("Messages.app")
end)
self.launcher:bind("", "O", function()
self:switch("@obsidian@")
end)
self.launcher:bind("", "P", function()
self:switch("System Preferences.app")
end)
self.launcher:bind("shift", "P", function()
hs.execute("launchctl load /Library/LaunchAgents/com.paloaltonetworks.gp.pangps.plist")
hs.execute("launchctl load /Library/LaunchAgents/com.paloaltonetworks.gp.pangpa.plist")
hs.alert.show("Reconnecting to GlobalProtect", nil, nil, 4)
self.launcher:exit()
end)
self.launcher:bind("", "R", function()
hs.console.clearConsole()
hs.reload()
end)
self.launcher:bind("", "S", function()
self:switch("@slack@")
end)
self.launcher:bind("", "Z", function()
self:switch("zoom.us.app")
end)
end
function obj:switch(app)
hs.application.launchOrFocus(app)
self.launcher:exit()
end
return obj

View File

@ -1,81 +0,0 @@
--- === Move Window ===
local obj = {}
obj.__index = obj
-- Metadata
obj.name = "MoveWindow"
obj.version = "0.1"
obj.license = "MIT - https://opensource.org/licenses/MIT"
function obj:init()
hs.window.animationDuration = 0.1
dofile(hs.spoons.resourcePath("worklayout.lua"))()
-- bind hotkey
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "n", function()
-- get the focused window
local win = hs.window.focusedWindow()
-- get the screen where the focused window is displayed, a.k.a. current screen
local screen = win:screen()
-- local nextScreen = screen:next()
-- compute the unitRect of the focused window relative to the current screen
-- and move the window to the next screen setting the same unitRect
win:moveToScreen(screen:next(), true, true, 0)
end)
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "b", function()
local win = hs.window.focusedWindow()
local screen = win:screen()
win:moveToScreen(screen:previous(), true, true, 0)
end)
-- Maximize
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "m", function()
-- get the focused window
local win = hs.window.focusedWindow()
local frame = win:frame()
-- maximize if possible
local max = win:screen():fullFrame()
frame.x = max.x
frame.y = max.y
frame.w = max.w
frame.h = max.h
win:setFrame(frame)
-- -- first maximize to grid
-- hs.grid.maximizeWindow(win)
-- -- then spam maximize
-- for i = 1, 8 do
-- win:maximize()
-- end
end)
-- Half-maximize (right)
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "o", function()
-- get the focused window
local win = hs.window.focusedWindow()
local frame = win:frame()
-- maximize if possible
local max = win:screen():fullFrame()
frame.x = (max.x * 2 + max.w) / 2
frame.y = max.y
frame.w = max.w / 2
frame.h = max.h
win:setFrame(frame)
end)
-- Half-maximize (left)
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "u", function()
-- get the focused window
local win = hs.window.focusedWindow()
local frame = win:frame()
-- maximize if possible
local max = win:screen():fullFrame()
frame.x = max.x
frame.y = max.y
frame.w = max.w / 2
frame.h = max.h
win:setFrame(frame)
end)
end
return obj

View File

@ -1,70 +0,0 @@
--- === Work Layout ===
-- Portions of this is adopted from:
-- https://github.com/anishathalye/dotfiles-local/tree/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/hammerspoon
-- License is also available:
-- https://github.com/anishathalye/dotfiles-local/blob/ffdadd313e58514eb622736b09b91a7d7eb7c6c9/LICENSE.md
WORK_ONLY_MONITOR = "DELL U4021QW"
LAPTOP_MONITOR = "Built-in Retina Display"
-- Used to find out the name of the monitor in Hammerspoon
local function dump(o)
if type(o) == "table" then
local s = "{ "
for k, v in pairs(o) do
if type(k) ~= "number" then
k = '"' .. k .. '"'
end
s = s .. "[" .. k .. "] = " .. dump(v) .. ","
end
return s .. "} "
else
return tostring(o)
end
end
-- Turn on when looking for the monitor name
print(dump(hs.screen.allScreens()))
local function concat(...)
local res = {}
for _, tab in ipairs({ ... }) do
for _, elem in ipairs(tab) do
table.insert(res, elem)
end
end
return res
end
local function worklayout()
hs.hotkey.bind({ "alt", "ctrl", "cmd" }, "l", function()
local u = hs.geometry.unitrect
-- set the layout
local left = {
{ "WezTerm", nil, WORK_ONLY_MONITOR, u(0, 0, 1 / 2, 1), nil, nil, visible = true },
}
local right = {
{ "Slack", nil, WORK_ONLY_MONITOR, u(1 / 2, 0, 1 / 2, 1), nil, nil, visible = true },
{ "Mail", nil, WORK_ONLY_MONITOR, u(1 / 2, 0, 1 / 2, 1), nil, nil, visible = true },
{ "zoom.us", nil, WORK_ONLY_MONITOR, u(5 / 8, 1 / 4, 1 / 4, 1 / 2), nil, nil, visible = true },
}
local laptop = {
{ "Firefox", nil, LAPTOP_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
{ "Obsidian", nil, LAPTOP_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
{ "Calendar", nil, LAPTOP_MONITOR, u(0, 0, 1, 1), nil, nil, visible = true },
}
local layout = concat(left, right, laptop)
hs.layout.apply(layout)
end)
-- Reload Hammerspoon whenever layout changes
hs.screen.watcher.new(function()
-- Pause for 5 seconds to give time for layout to change
hs.timer.doAfter(5, function()
-- Perform the actual reload
hs.reload()
end)
end)
end
return worklayout

Some files were not shown because too many files have changed in this diff Show More