diff --git a/README.md b/README.md
index d369d73..dc34000 100644
--- a/README.md
+++ b/README.md
@@ -26,11 +26,42 @@ Install all packages:
yay -S $(grep -v '^\s*$' ~/scripts/packages.txt | tr '\n' ' ')
```
-## Linking Configs
+## Install / Uninstall
-Pick a window manager section below, then add the shared configs.
+`install.sh` symlinks configs, sources shell files, and backs up any existing files it would overwrite. `uninstall.sh` reverses the process, removing symlinks and restoring backups.
-### Hyprland
+```bash
+# Install everything (uses Hyprland as the WM)
+~/scripts/install.sh all
+
+# Install specific targets
+~/scripts/install.sh hyprland shell nvim tmux
+
+# Uninstall specific targets (restores from most recent backup)
+~/scripts/uninstall.sh nvim shell
+
+# Uninstall using a specific backup
+~/scripts/uninstall.sh --backup ~/scripts/backups/20260319_120000 all
+```
+
+Available targets: `hyprland`, `sway`, `i3`, `cursor`, `shell`, `alacritty`, `tmux`, `nvim`, `vim`, `all`.
+
+### tmux prerequisite
+
+Install the plugin manager before running `install.sh tmux`:
+
+```bash
+git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
+```
+
+### Manual linking
+
+If you prefer to link configs manually instead of using `install.sh`:
+
+
+Manual symlink commands
+
+#### Hyprland
```bash
ln -sf ~/scripts/hypr ~/.config/hypr
@@ -38,7 +69,7 @@ ln -sf ~/scripts/waybar ~/.config/waybar
ln -sf ~/scripts/wofi ~/.config/wofi
```
-### Sway
+#### Sway
```bash
ln -sf ~/scripts/sway ~/.config/sway
@@ -53,7 +84,7 @@ mv ~/scripts/waybar/config ~/scripts/waybar/config.hypr.json
mv ~/scripts/waybar/waybar_sway_config.json ~/scripts/waybar/config
```
-### i3
+#### i3
```bash
ln -sf ~/scripts/i3 ~/.config/i3
@@ -61,13 +92,13 @@ ln -sf ~/scripts/i3blocks ~/.config/i3blocks
ln -sf ~/scripts/i3status ~/.config/i3status
```
-### Cursor
+#### Cursor
```bash
ln -sf ~/scripts/hatsune-miku-windows-linux-cursors/miku-cursor-linux ~/.local/share/icons/"Miku Cursor"
```
-### Shell
+#### Shell
Add to `~/.bashrc` or `~/.zshrc`:
@@ -76,7 +107,7 @@ source ~/scripts/bashrc # or zshrc
export PATH="~/scripts/sh:$PATH"
```
-### Terminals
+#### Terminals
**Alacritty** - add to `~/.config/alacritty/alacritty.toml`:
@@ -86,13 +117,7 @@ import = ["~/scripts/alacritty.toml"]
**Kitty** - kitty.conf is used directly by the WM configs.
-### tmux
-
-Install the plugin manager first:
-
-```bash
-git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
-```
+#### tmux
Add to `~/.tmux.conf`:
@@ -100,7 +125,7 @@ Add to `~/.tmux.conf`:
source ~/scripts/tmux.conf
```
-### Vim
+#### Vim
Add to `~/.vimrc`:
@@ -108,6 +133,8 @@ Add to `~/.vimrc`:
source ~/scripts/vimrc
```
+
+
## Keybindings
All window managers and tmux use vim-style hjkl navigation. `$mod` is the Super key.
diff --git a/install.sh b/install.sh
index df9107c..79a3185 100755
--- a/install.sh
+++ b/install.sh
@@ -100,11 +100,11 @@ install_cursor() {
install_shell() {
echo "Installing shell configs..."
- if [ -f "$HOME/.zshrc" ] || [ "$SHELL" = *zsh* ]; then
+ if [ -f "$HOME/.zshrc" ] || [[ "$SHELL" == *zsh* ]]; then
append_if_missing "$HOME/.zshrc" "source ~/scripts/zshrc"
append_if_missing "$HOME/.zshrc" 'export PATH="$HOME/scripts/sh:$PATH"'
fi
- if [ -f "$HOME/.bashrc" ] || [ "$SHELL" = *bash* ]; then
+ if [ -f "$HOME/.bashrc" ] || [[ "$SHELL" == *bash* ]]; then
append_if_missing "$HOME/.bashrc" "source ~/scripts/bashrc"
append_if_missing "$HOME/.bashrc" 'export PATH="$HOME/scripts/sh:$PATH"'
fi
diff --git a/nvim/.luarc.json b/nvim/.luarc.json
new file mode 100644
index 0000000..972d0f6
--- /dev/null
+++ b/nvim/.luarc.json
@@ -0,0 +1,12 @@
+{
+ "runtime": {
+ "version": "LuaJIT"
+ },
+ "diagnostics": {
+ "globals": ["vim", "LazyVim", "Snacks"]
+ },
+ "workspace": {
+ "library": ["${3rd}/luv/library"],
+ "checkThirdParty": false
+ }
+}
\ No newline at end of file
diff --git a/nvim/lazyvim.json b/nvim/lazyvim.json
index e375555..8d59136 100644
--- a/nvim/lazyvim.json
+++ b/nvim/lazyvim.json
@@ -1,6 +1,5 @@
{
"extras": [
- "lazyvim.plugins.extras.ai.claudecode",
"lazyvim.plugins.extras.coding.mini-surround",
"lazyvim.plugins.extras.dap.core",
"lazyvim.plugins.extras.dap.nlua",
diff --git a/nvim/lua/config/autocmds.lua b/nvim/lua/config/autocmds.lua
index 4221e75..2ec3018 100644
--- a/nvim/lua/config/autocmds.lua
+++ b/nvim/lua/config/autocmds.lua
@@ -6,3 +6,12 @@
--
-- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults)
-- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell")
+
+-- When opening multiple files, tile them vertically (side by side)
+vim.api.nvim_create_autocmd("VimEnter", {
+ callback = function()
+ if vim.fn.argc() == 2 then
+ vim.cmd("vertical ball")
+ end
+ end,
+})
diff --git a/nvim/lua/config/keymaps.lua b/nvim/lua/config/keymaps.lua
index 2c134f7..3c4adcc 100644
--- a/nvim/lua/config/keymaps.lua
+++ b/nvim/lua/config/keymaps.lua
@@ -1,3 +1,15 @@
-- Keymaps are automatically loaded on the VeryLazy event
-- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua
-- Add any additional keymaps here
+
+-- Terminal: open relative to current window (don't squash explorer) and smaller
+local terminal_opts = {
+ cwd = LazyVim.root(),
+ win = { position = "bottom", height = 0.3, relative = "win" },
+}
+vim.keymap.set({ "n", "t" }, "", function()
+ Snacks.terminal(nil, terminal_opts)
+end, { desc = "Terminal (Root Dir)" })
+vim.keymap.set({ "n", "t" }, "", function()
+ Snacks.terminal(nil, terminal_opts)
+end, { desc = "which_key_ignore" })
diff --git a/nvim/lua/config/options.lua b/nvim/lua/config/options.lua
index 3ea1454..72b50db 100644
--- a/nvim/lua/config/options.lua
+++ b/nvim/lua/config/options.lua
@@ -1,3 +1,6 @@
-- Options are automatically loaded before lazy.nvim startup
-- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua
-- Add any additional options here
+vim.opt.tabstop = 4
+vim.opt.shiftwidth = 4
+vim.opt.softtabstop = 4
diff --git a/nvim/lua/plugins/claudecode.lua b/nvim/lua/plugins/claudecode.lua
index 2b10879..8233edd 100644
--- a/nvim/lua/plugins/claudecode.lua
+++ b/nvim/lua/plugins/claudecode.lua
@@ -1,26 +1,64 @@
return {
- {
- "coder/claudecode.nvim",
- dependencies = { "folke/snacks.nvim" },
- config = true,
- keys = {
- { "a", nil, desc = "AI/Claude Code" },
- { "ac", "ClaudeCode", desc = "Toggle Claude" },
- { "af", "ClaudeCodeFocus", desc = "Focus Claude" },
- { "ar", "ClaudeCode --resume", desc = "Resume Claude" },
- { "aC", "ClaudeCode --continue", desc = "Continue Claude" },
- { "am", "ClaudeCodeSelectModel", desc = "Select Claude model" },
- { "ab", "ClaudeCodeAdd %", desc = "Add current buffer" },
- { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" },
- {
- "as",
- "ClaudeCodeTreeAdd",
- desc = "Add file",
- ft = { "NvimTree", "neo-tree", "oil", "minifiles", "netrw" },
- },
- -- Diff management
- { "aa", "ClaudeCodeDiffAccept", desc = "Accept diff" },
- { "ad", "ClaudeCodeDiffDeny", desc = "Deny diff" },
- },
- },
+ {
+ "coder/claudecode.nvim",
+ dependencies = { "folke/snacks.nvim" },
+ opts = {
+ terminal = {
+ provider = "snacks",
+ snacks_win_opts = {
+ relative = "win",
+ position = "bottom",
+ height = 0.3,
+ split_width_percentage = 0.99,
+ },
+ },
+ diff_opts = {
+ layout = "vertical",
+ open_in_new_tab = true,
+ keep_terminal_focus = true,
+ },
+ },
+ keys = {
+ { "a", "", desc = "+ai", mode = { "n", "v" } },
+ { "ac", "ClaudeCode", desc = "Toggle Claude" },
+ { "af", "ClaudeCodeFocus", desc = "Focus Claude" },
+ { "ar", "ClaudeCode --resume", desc = "Resume Claude" },
+ { "aC", "ClaudeCode --continue", desc = "Continue Claude" },
+ { "ab", "ClaudeCodeAdd %", desc = "Add current buffer" },
+ { "as", "ClaudeCodeSend", mode = "v", desc = "Send to Claude" },
+ {
+ "as",
+ "ClaudeCodeTreeAdd",
+ desc = "Add file",
+ ft = { "NvimTree", "neo-tree", "oil" },
+ },
+ -- Diff management
+ { "aa", "ClaudeCodeDiffAccept", desc = "Accept diff" },
+ { "ad", "ClaudeCodeDiffDeny", desc = "Deny diff" },
+ },
+ },
+
+ -- Disabled: unmaintained
+ {
+ "greggh/claude-code.nvim",
+ enabled = false,
+ dependencies = { "nvim-lua/plenary.nvim" },
+ opts = {
+ position = "float",
+ float_opts = {
+ relative = "win",
+ width = "80%",
+ height = "80%",
+ row = "center",
+ col = "center",
+ border = "rounded",
+ },
+ },
+ keys = {
+ { "a", nil, desc = "AI/Claude Code" },
+ { "ac", "ClaudeCode", desc = "Toggle Claude" },
+ { "ar", "ClaudeCodeResume", desc = "Resume Claude" },
+ { "aC", "ClaudeCodeContinue", desc = "Continue Claude" },
+ },
+ },
}
diff --git a/nvim/lua/plugins/colorscheme.lua b/nvim/lua/plugins/colorscheme.lua
index 5167df4..b1a39ea 100644
--- a/nvim/lua/plugins/colorscheme.lua
+++ b/nvim/lua/plugins/colorscheme.lua
@@ -1,3 +1,37 @@
return {
- { "ellisonleao/gruvbox.nvim" },
+ {
+ "nyoom-engineering/oxocarbon.nvim",
+ lazy = false,
+ priority = 1000,
+ config = function()
+ vim.opt.background = "dark"
+ vim.cmd("colorscheme oxocarbon")
+ end,
+ },
+ {
+ "sainnhe/sonokai",
+ opts = {
+ sonokai_style = "atlantis",
+ sonokai_transparent_background = 1,
+ sonokai_enable_italic = true,
+ },
+ },
+ {
+ "folke/tokyonight.nvim",
+ opts = {
+ style = "night",
+ transparent = true,
+ styles = { comments = { italic = true } },
+ },
+ },
+ {
+ "loctvl842/monokai-pro.nvim",
+ opts = {
+ transparent_background = true,
+ filter = "spectrum",
+ },
+ },
+ { "scottmckendry/cyberdream.nvim" },
+ { "maxmx03/fluoromachine.nvim" },
+ { "rafamadriz/neon" },
}
diff --git a/nvim/lua/plugins/snacks.lua b/nvim/lua/plugins/snacks.lua
index 249c0e7..638a05b 100644
--- a/nvim/lua/plugins/snacks.lua
+++ b/nvim/lua/plugins/snacks.lua
@@ -1,13 +1,13 @@
return {
- {
- "snacks.nvim",
- opts = {
- dashboard = {
- preset = {
- pick = function(cmd, opts)
- return LazyVim.pick(cmd, opts)()
- end,
- header = [[
+ {
+ "snacks.nvim",
+ opts = {
+ dashboard = {
+ preset = {
+ pick = function(cmd, opts)
+ return LazyVim.pick(cmd, opts)()
+ end,
+ header = [[
███████╗████████╗██████╗
██╔════╝╚══██╔══╝██╔══██╗
███████╗ ██║ ██████╔╝
@@ -16,7 +16,6 @@ return {
╚══════╝ ╚═╝ ╚═╝
]],
-- stylua: ignore
- ---@type snacks.dashboard.Item[]
keys = {
{ icon = " ", key = "f", desc = "Find File", action = ":lua Snacks.dashboard.pick('files')" },
{ icon = " ", key = "n", desc = "New File", action = ":ene | startinsert" },
@@ -28,8 +27,8 @@ return {
{ icon = " ", key = "l", desc = "Lazy", action = ":Lazy" },
{ icon = " ", key = "q", desc = "Quit", action = ":qa" },
},
- },
- },
- },
- },
+ },
+ },
+ },
+ },
}
diff --git a/nvim/lua/plugins/vimade.lua b/nvim/lua/plugins/vimade.lua
new file mode 100644
index 0000000..1daa2f9
--- /dev/null
+++ b/nvim/lua/plugins/vimade.lua
@@ -0,0 +1,13 @@
+return {
+ "TaDaa/vimade",
+ event = "WinNew",
+ opts = {
+ recipe = { "minimalist", { animate = true } },
+ fadelevel = 0.8,
+ blocklist = {
+ claude_terminal = {
+ buf_opts = { ft = "snacks_terminal" },
+ },
+ },
+ },
+}
diff --git a/nvim/stylua.toml b/nvim/stylua.toml
deleted file mode 100644
index 5d6c50d..0000000
--- a/nvim/stylua.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-indent_type = "Spaces"
-indent_width = 2
-column_width = 120
\ No newline at end of file
diff --git a/uninstall.sh b/uninstall.sh
new file mode 100755
index 0000000..b919e9b
--- /dev/null
+++ b/uninstall.sh
@@ -0,0 +1,216 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPTS_DIR="$HOME/scripts"
+CONFIG_DIR="$HOME/.config"
+BACKUP_DIR=""
+
+usage() {
+ echo "Usage: $(basename "$0") [OPTIONS] ..."
+ echo
+ echo "Targets:"
+ echo " hyprland Hyprland + waybar + wofi"
+ echo " sway Sway + waybar + wofi"
+ echo " i3 i3 + i3blocks + i3status"
+ echo " cursor Miku cursor theme"
+ echo " shell Remove sourced bashrc/zshrc and sh/ from PATH"
+ echo " alacritty Remove import from alacritty.toml"
+ echo " tmux Remove sourced tmux.conf"
+ echo " nvim Neovim (LazyVim) config"
+ echo " vim Remove sourced vimrc"
+ echo " all Everything (uses hyprland as WM)"
+ echo
+ echo "Options:"
+ echo " -h, --help Show this help"
+ echo " --backup Use specific backup directory for restoring"
+ echo " (default: most recent in $SCRIPTS_DIR/backups/)"
+}
+
+# Find the backup directory to restore from
+find_backup() {
+ if [ -n "$BACKUP_DIR" ]; then
+ if [ ! -d "$BACKUP_DIR" ]; then
+ echo "Error: backup directory not found: $BACKUP_DIR"
+ exit 1
+ fi
+ return
+ fi
+ local backups_root="$SCRIPTS_DIR/backups"
+ if [ -d "$backups_root" ]; then
+ local latest
+ latest=$(ls -1d "$backups_root"/*/ 2>/dev/null | sort | tail -n1 || true)
+ if [ -n "$latest" ]; then
+ BACKUP_DIR="${latest%/}"
+ echo "Using backup: $BACKUP_DIR"
+ fi
+ fi
+ if [ -z "$BACKUP_DIR" ]; then
+ echo "No backups found, will only remove without restoring"
+ fi
+}
+
+# Remove a symlink if it points into $SCRIPTS_DIR, then restore from backup if available
+restore() {
+ local dest="$1"
+ local name
+ name=$(basename "$dest")
+
+ if [ -L "$dest" ]; then
+ local target
+ target=$(readlink "$dest")
+ if [[ "$target" == "$SCRIPTS_DIR"* ]]; then
+ rm "$dest"
+ echo " Removed symlink $dest"
+ else
+ echo " Skipping $dest (symlink does not point into $SCRIPTS_DIR)"
+ return
+ fi
+ elif [ -e "$dest" ]; then
+ echo " Skipping $dest (not a symlink, won't remove)"
+ return
+ else
+ echo " $dest does not exist, nothing to remove"
+ fi
+
+ # Restore from backup if available
+ if [ -n "$BACKUP_DIR" ] && [ -e "$BACKUP_DIR/$name" ]; then
+ cp -a "$BACKUP_DIR/$name" "$dest"
+ echo " Restored $dest from backup"
+ fi
+}
+
+# Remove exact matching lines from a file
+remove_line() {
+ local file="$1" line="$2"
+ if [ ! -f "$file" ]; then
+ echo " $file does not exist, nothing to remove"
+ return
+ fi
+ if grep -qF "$line" "$file"; then
+ local tmp
+ tmp=$(mktemp)
+ grep -vF "$line" "$file" > "$tmp" || true
+ mv "$tmp" "$file"
+ echo " Removed from $file: $line"
+ # Delete file if only whitespace remains
+ if [ ! -s "$file" ] || ! grep -q '[^[:space:]]' "$file"; then
+ rm "$file"
+ echo " Deleted empty $file"
+ fi
+ else
+ echo " Line not found in $file: $line"
+ fi
+}
+
+uninstall_hyprland() {
+ echo "Uninstalling Hyprland configs..."
+ restore "$CONFIG_DIR/hypr"
+ restore "$CONFIG_DIR/waybar"
+ restore "$CONFIG_DIR/wofi"
+}
+
+uninstall_sway() {
+ echo "Uninstalling Sway configs..."
+ restore "$CONFIG_DIR/sway"
+ restore "$CONFIG_DIR/waybar"
+ restore "$CONFIG_DIR/wofi"
+}
+
+uninstall_i3() {
+ echo "Uninstalling i3 configs..."
+ restore "$CONFIG_DIR/i3"
+ restore "$CONFIG_DIR/i3blocks"
+ restore "$CONFIG_DIR/i3status"
+}
+
+uninstall_cursor() {
+ echo "Uninstalling cursor theme..."
+ restore "$HOME/.local/share/icons/Miku Cursor"
+}
+
+uninstall_shell() {
+ echo "Uninstalling shell configs..."
+ if [ -f "$HOME/.zshrc" ]; then
+ remove_line "$HOME/.zshrc" "source ~/scripts/zshrc"
+ remove_line "$HOME/.zshrc" 'export PATH="$HOME/scripts/sh:$PATH"'
+ fi
+ if [ -f "$HOME/.bashrc" ]; then
+ remove_line "$HOME/.bashrc" "source ~/scripts/bashrc"
+ remove_line "$HOME/.bashrc" 'export PATH="$HOME/scripts/sh:$PATH"'
+ fi
+}
+
+uninstall_alacritty() {
+ echo "Uninstalling Alacritty config..."
+ local conf="$CONFIG_DIR/alacritty/alacritty.toml"
+ remove_line "$conf" 'import = ["~/scripts/alacritty.toml"]'
+}
+
+uninstall_tmux() {
+ echo "Uninstalling tmux config..."
+ remove_line "$HOME/.tmux.conf" "source ~/scripts/tmux.conf"
+}
+
+uninstall_nvim() {
+ echo "Uninstalling Neovim config..."
+ restore "$CONFIG_DIR/nvim"
+}
+
+uninstall_vim() {
+ echo "Uninstalling vim config..."
+ remove_line "$HOME/.vimrc" "source ~/scripts/vimrc"
+}
+
+if [ $# -eq 0 ]; then
+ usage
+ exit 1
+fi
+
+targets=()
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -h | --help)
+ usage
+ exit 0
+ ;;
+ --backup)
+ if [ $# -lt 2 ]; then
+ echo "Error: --backup requires a directory argument"
+ exit 1
+ fi
+ BACKUP_DIR="$2"
+ shift 2
+ ;;
+ all)
+ targets+=(hyprland cursor shell alacritty tmux nvim vim)
+ shift
+ ;;
+ *)
+ targets+=("$1")
+ shift
+ ;;
+ esac
+done
+
+find_backup
+
+for target in "${targets[@]}"; do
+ case "$target" in
+ hyprland) uninstall_hyprland ;;
+ sway) uninstall_sway ;;
+ i3) uninstall_i3 ;;
+ cursor) uninstall_cursor ;;
+ shell) uninstall_shell ;;
+ alacritty) uninstall_alacritty ;;
+ tmux) uninstall_tmux ;;
+ nvim) uninstall_nvim ;;
+ vim) uninstall_vim ;;
+ *)
+ echo "Unknown target: $target"
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+echo "Done!"