├── yazi ├── plugins │ ├── smart-paste.yazi │ │ ├── LICENSE │ │ ├── main.lua │ │ └── README.md │ └── clipboard.yazi │ │ ├── README.md │ │ ├── main.lua │ │ └── LICENSE ├── package.toml ├── keymap.toml └── yazi.toml ├── nvim ├── stylua.toml ├── init.lua ├── lazyvim.json ├── lua │ ├── config │ │ ├── keymaps.lua │ │ ├── autocmds.lua │ │ ├── lazy.lua │ │ └── options.lua │ └── plugins │ │ ├── snacks-terminal.lua │ │ ├── treesitter.lua │ │ ├── snacks-explorer.lua │ │ └── example.lua └── lazy-lock.json ├── .pre-commit-config.yaml ├── ghostty ├── tmux-attach.sh └── config ├── .gitleaks.toml ├── secrets.env.template ├── .gitignore ├── rollback.sh ├── scripts ├── com.dotfiles.autosync.plist ├── sync-status.sh └── auto-sync.sh ├── Brewfile ├── install.sh ├── zsh └── .zshrc ├── README.md ├── aerospace └── aerospace.toml └── wezterm └── wezterm.lua /yazi/plugins/smart-paste.yazi/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /nvim/stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 2 3 | column_width = 120 -------------------------------------------------------------------------------- /nvim/init.lua: -------------------------------------------------------------------------------- 1 | -- bootstrap lazy.nvim, LazyVim and your plugins 2 | require("config.lazy") 3 | -------------------------------------------------------------------------------- /nvim/lazyvim.json: -------------------------------------------------------------------------------- 1 | { 2 | "extras": [ 3 | 4 | ], 5 | "install_version": 8, 6 | "news": { 7 | "NEWS.md": "11866" 8 | }, 9 | "version": 8 10 | } -------------------------------------------------------------------------------- /nvim/lua/config/keymaps.lua: -------------------------------------------------------------------------------- 1 | -- Keymaps are automatically loaded on the VeryLazy event 2 | -- Default keymaps that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/keymaps.lua 3 | -- Add any additional keymaps here 4 | -------------------------------------------------------------------------------- /yazi/package.toml: -------------------------------------------------------------------------------- 1 | [[plugin.deps]] 2 | use = "yazi-rs/plugins:smart-paste" 3 | rev = "8f1d971" 4 | hash = "75fd60b2df7115e0a6ca31a6a804d320" 5 | 6 | [[plugin.deps]] 7 | use = "XYenon/clipboard" 8 | rev = "9089f90" 9 | hash = "e6efc609de1b617c7e8e98efd5286221" 10 | 11 | [flavor] 12 | deps = [] 13 | -------------------------------------------------------------------------------- /yazi/plugins/smart-paste.yazi/main.lua: -------------------------------------------------------------------------------- 1 | --- @since 25.5.31 2 | --- @sync entry 3 | return { 4 | entry = function() 5 | local h = cx.active.current.hovered 6 | if h and h.cha.is_dir then 7 | ya.emit("enter", {}) 8 | ya.emit("paste", {}) 9 | ya.emit("leave", {}) 10 | else 11 | ya.emit("paste", {}) 12 | end 13 | end, 14 | } 15 | -------------------------------------------------------------------------------- /nvim/lua/plugins/snacks-terminal.lua: -------------------------------------------------------------------------------- 1 | -- Snacks Terminal Configuration 2 | -- Configure the terminal to open as a floating window 3 | -- Triggered with (Ctrl+/) 4 | 5 | return { 6 | { 7 | "folke/snacks.nvim", 8 | opts = { 9 | terminal = { 10 | win = { 11 | position = "float", 12 | }, 13 | }, 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /nvim/lua/plugins/treesitter.lua: -------------------------------------------------------------------------------- 1 | -- Configure treesitter to auto-install parsers 2 | return { 3 | { 4 | "nvim-treesitter/nvim-treesitter", 5 | opts = { 6 | ensure_installed = { 7 | "bash", 8 | "css", 9 | "html", 10 | "javascript", 11 | "json", 12 | "lua", 13 | "markdown", 14 | "markdown_inline", 15 | "python", 16 | "tsx", 17 | "typescript", 18 | "vim", 19 | "yaml", 20 | }, 21 | }, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # Pre-commit hooks for dotfiles secret protection 2 | # Run manually: pre-commit run --all-files 3 | # Auto-runs on: git commit 4 | 5 | repos: 6 | # Gitleaks - detect secrets in git repos 7 | - repo: https://github.com/gitleaks/gitleaks 8 | rev: v8.21.2 9 | hooks: 10 | - id: gitleaks 11 | name: gitleaks 12 | description: Detect hardcoded secrets 13 | entry: gitleaks protect --verbose --redact --staged 14 | language: system 15 | pass_filenames: false 16 | -------------------------------------------------------------------------------- /ghostty/tmux-attach.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Smart tmux session management for Ghostty 3 | # - First window: attach to existing detached session (resume work) 4 | # - Additional windows: create new sessions 5 | 6 | TMUX=/opt/homebrew/bin/tmux 7 | 8 | # Find first detached session (not attached by any client) 9 | DETACHED=$($TMUX ls 2>/dev/null | grep -v "(attached)" | head -1 | cut -d: -f1) 10 | 11 | if [ -n "$DETACHED" ]; then 12 | # Detached session exists - attach to it (resume work) 13 | exec $TMUX attach-session -t "$DETACHED" 14 | else 15 | # No detached sessions - create a new one 16 | exec $TMUX new-session 17 | fi 18 | -------------------------------------------------------------------------------- /yazi/keymap.toml: -------------------------------------------------------------------------------- 1 | [[mgr.prepend_keymap]] 2 | on = "p" 3 | run = "plugin smart-paste" 4 | desc = "Paste into the hovered directory or CWD" 5 | 6 | # macOS Finder integration 7 | [[mgr.prepend_keymap]] 8 | on = ["g", "r"] 9 | run = 'shell "open -R \"$0\""' 10 | desc = "Reveal in Finder" 11 | 12 | # Yank operations menu (y prefix) 13 | # yy = internal yank (for paste with p) 14 | # yc = yank + copy to Finder clipboard (pasteable in Finder) 15 | [[mgr.prepend_keymap]] 16 | on = ["y", "y"] 17 | run = "yank" 18 | desc = "Yank selected files" 19 | 20 | [[mgr.prepend_keymap]] 21 | on = ["y", "c"] 22 | run = ["yank", "plugin clipboard -- --action=copy"] 23 | desc = "Copy to Finder clipboard" 24 | -------------------------------------------------------------------------------- /yazi/yazi.toml: -------------------------------------------------------------------------------- 1 | # Use Neovim for text files 2 | [[opener.edit]] 3 | run = 'nvim "$@"' 4 | block = true # yazi waits until you quit nvim 5 | for = "unix" 6 | 7 | [open] 8 | prepend_rules = [ 9 | { mime = "text/*", use = "edit" }, 10 | { name = "*.md", use = "edit" }, 11 | { name = "*.txt", use = "edit" }, 12 | { name = "*.json", use = "edit" }, 13 | { name = "*.yaml", use = "edit" }, 14 | { name = "*.yml", use = "edit" }, 15 | { name = "*.toml", use = "edit" }, 16 | { name = "*.py", use = "edit" }, 17 | { name = "*.js", use = "edit" }, 18 | { name = "*.ts", use = "edit" }, 19 | { name = "*.rs", use = "edit" }, 20 | { name = "*.go", use = "edit" }, 21 | ] 22 | -------------------------------------------------------------------------------- /yazi/plugins/smart-paste.yazi/README.md: -------------------------------------------------------------------------------- 1 | # smart-paste.yazi 2 | 3 | Paste files into the hovered directory or to the CWD if hovering over a file. 4 | 5 | https://github.com/user-attachments/assets/b3f6348e-abbe-42fe-9a67-a96e68f11255 6 | 7 | ## Installation 8 | 9 | ```sh 10 | ya pkg add yazi-rs/plugins:smart-paste 11 | ``` 12 | 13 | ## Usage 14 | 15 | Add this to your `~/.config/yazi/keymap.toml`: 16 | 17 | ```toml 18 | [[mgr.prepend_keymap]] 19 | on = "p" 20 | run = "plugin smart-paste" 21 | desc = "Paste into the hovered directory or CWD" 22 | ``` 23 | 24 | Note that, the keybindings above are just examples, please tune them up as needed to ensure they don't conflict with your other commands/plugins. 25 | 26 | ## License 27 | 28 | This plugin is MIT-licensed. For more information check the [LICENSE](LICENSE) file. 29 | -------------------------------------------------------------------------------- /.gitleaks.toml: -------------------------------------------------------------------------------- 1 | # Gitleaks configuration for dotfiles 2 | # Extends default rules with allowlist for false positives 3 | 4 | [extend] 5 | # Use default gitleaks rules 6 | useDefault = true 7 | 8 | [allowlist] 9 | description = "Allowlist for dotfiles false positives" 10 | 11 | # Paths to completely ignore (these files never contain secrets) 12 | paths = [ 13 | '''lazy-lock\.json''', 14 | '''package\.toml''', 15 | '''Brewfile\.lock\.json''', 16 | ] 17 | 18 | # Regex patterns to ignore (common false positives) 19 | regexes = [ 20 | # Example placeholder values in configs 21 | '''YOUR_API_KEY_HERE''', 22 | '''your-api-key''', 23 | '''''', 24 | '''REPLACE_ME''', 25 | # Hex color codes that look like tokens 26 | '''#[0-9a-fA-F]{6}''', 27 | # Common example domains/URLs 28 | '''example\.com''', 29 | '''localhost''', 30 | ] 31 | 32 | # Specific commits to ignore (add SHA here if needed) 33 | commits = [] 34 | -------------------------------------------------------------------------------- /nvim/lua/config/autocmds.lua: -------------------------------------------------------------------------------- 1 | -- Autocmds are automatically loaded on the VeryLazy event 2 | -- Default autocmds that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/autocmds.lua 3 | -- 4 | -- Add any additional autocmds here 5 | -- with `vim.api.nvim_create_autocmd` 6 | -- 7 | -- Or remove existing autocmds by their group name (which is prefixed with `lazyvim_` for the defaults) 8 | -- e.g. vim.api.nvim_del_augroup_by_name("lazyvim_wrap_spell") 9 | 10 | -- FIX: Disable focus reporting (DECSET 1004) when leaving nvim 11 | -- Prevents [I[O escape sequences from leaking into claude-code after Ctrl+G editing 12 | -- See: https://github.com/anthropics/claude-code/issues/10375 13 | vim.api.nvim_create_autocmd("VimLeave", { 14 | callback = function() 15 | local tty = io.open("/dev/tty", "w") 16 | if tty then 17 | tty:write("\027[?1004l") 18 | tty:flush() 19 | tty:close() 20 | end 21 | end, 22 | desc = "Disable focus reporting on nvim exit for claude-code compatibility", 23 | }) 24 | -------------------------------------------------------------------------------- /secrets.env.template: -------------------------------------------------------------------------------- 1 | # ============================================================================= 2 | # SECRETS TEMPLATE 3 | # ============================================================================= 4 | # Copy this file to ~/.secrets.env and fill in your values. 5 | # The actual secrets.env file is gitignored and will NOT be committed. 6 | # 7 | # Usage: 8 | # 1. Copy: cp ~/.dotfiles/secrets.env.template ~/.secrets.env 9 | # 2. Edit: Fill in your actual values in ~/.secrets.env 10 | # 3. Source: Add to your ~/.zshrc: [ -f ~/.secrets.env ] && source ~/.secrets.env 11 | # ============================================================================= 12 | 13 | # GitHub 14 | export GITHUB_TOKEN="" 15 | 16 | # OpenAI 17 | export OPENAI_API_KEY="" 18 | 19 | # Anthropic 20 | export ANTHROPIC_API_KEY="" 21 | 22 | # Google/Gemini 23 | export GEMINI_API_KEY="" 24 | export GOOGLE_APPLICATION_CREDENTIALS="" 25 | 26 | # AWS 27 | export AWS_ACCESS_KEY_ID="" 28 | export AWS_SECRET_ACCESS_KEY="" 29 | export AWS_DEFAULT_REGION="" 30 | 31 | # Other API keys (add as needed) 32 | # export SOME_API_KEY="" 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Backup files 2 | *.backup 3 | *.bak 4 | *.bak.* 5 | .DS_Store 6 | 7 | # Yazi temp files 8 | .claude-history.md 9 | keymap.toml-* 10 | 11 | # Neovim runtime files 12 | .neoconf.json 13 | 14 | # ============================================================================= 15 | # SENSITIVE FILES - NEVER COMMIT 16 | # ============================================================================= 17 | 18 | # Environment files with secrets 19 | *.env 20 | .env.* 21 | !*.env.example 22 | !*.env.template 23 | secrets.env 24 | .secrets 25 | .secrets.* 26 | 27 | # API keys and tokens 28 | *_token 29 | *_token.* 30 | *.key 31 | *.pem 32 | !*.key.pub 33 | api_key* 34 | auth_token* 35 | 36 | # Credentials 37 | credentials.json 38 | credentials.yaml 39 | *.credentials 40 | .netrc 41 | .npmrc 42 | .pypirc 43 | 44 | # SSH (except public keys) 45 | id_rsa 46 | id_ed25519 47 | id_ecdsa 48 | *.ppk 49 | 50 | # AWS/Cloud 51 | .aws/credentials 52 | .aws/config 53 | *.tfvars 54 | *.tfstate* 55 | 56 | # GPG 57 | *.gpg 58 | secring.* 59 | pubring.* 60 | 61 | # Password stores 62 | .password-store/ 63 | *.kdbx 64 | 65 | # Local overrides (for machine-specific secrets) 66 | *.local 67 | *.local.* 68 | 69 | # Sync state files 70 | .last-sync 71 | .sync-status 72 | .sync.log 73 | 74 | # Zsh machine-specific overrides 75 | zsh/.zshrc.local 76 | -------------------------------------------------------------------------------- /rollback.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # rollback.sh - Restore configs from backup 3 | 4 | set -e 5 | 6 | if [ ! -f ~/.dotfiles-backup-location ]; then 7 | echo "Error: No backup location found. Cannot rollback." 8 | exit 1 9 | fi 10 | 11 | BACKUP_DIR=$(cat ~/.dotfiles-backup-location) 12 | 13 | if [ ! -d "$BACKUP_DIR" ]; then 14 | echo "Error: Backup directory $BACKUP_DIR does not exist." 15 | exit 1 16 | fi 17 | 18 | echo "Restoring from: $BACKUP_DIR" 19 | 20 | # Remove symlinks and dotfiles 21 | rm -rf ~/.dotfiles 22 | rm -f ~/.config/aerospace/aerospace.toml 23 | rm -f ~/.config/ghostty/config ~/.config/ghostty/tmux-attach.sh 24 | rm -f ~/.config/wezterm/wezterm.lua 25 | rm -f ~/.config/yazi/yazi.toml ~/.config/yazi/keymap.toml ~/.config/yazi/package.toml 26 | rm -rf ~/.config/yazi/plugins 27 | rm -f ~/.config/nvim/init.lua ~/.config/nvim/lazy-lock.json ~/.config/nvim/lazyvim.json ~/.config/nvim/stylua.toml 28 | rm -rf ~/.config/nvim/lua 29 | 30 | # Restore from backup 31 | cp -R "$BACKUP_DIR/aerospace/"* ~/.config/aerospace/ 32 | cp -R "$BACKUP_DIR/ghostty/"* ~/.config/ghostty/ 33 | cp -R "$BACKUP_DIR/wezterm/"* ~/.config/wezterm/ 34 | cp -R "$BACKUP_DIR/yazi/"* ~/.config/yazi/ 35 | cp -R "$BACKUP_DIR/nvim/"* ~/.config/nvim/ 36 | 37 | echo "Rollback complete! Configs restored from $BACKUP_DIR" 38 | echo "You can delete the backup with: rm -rf $BACKUP_DIR ~/.dotfiles-backup-location" 39 | -------------------------------------------------------------------------------- /scripts/com.dotfiles.autosync.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.dotfiles.autosync 7 | 8 | ProgramArguments 9 | 10 | /bin/bash 11 | -c 12 | $HOME/.dotfiles/scripts/auto-sync.sh 13 | 14 | 15 | 16 | StartInterval 17 | 3600 18 | 19 | 20 | RunAtLoad 21 | 22 | 23 | 24 | EnvironmentVariables 25 | 26 | PATH 27 | /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin 28 | 29 | 30 | 31 | StandardOutPath 32 | /tmp/dotfiles-sync.stdout.log 33 | StandardErrorPath 34 | /tmp/dotfiles-sync.stderr.log 35 | 36 | 37 | KeepAlive 38 | 39 | SuccessfulExit 40 | 41 | 42 | 43 | 44 | Nice 45 | 10 46 | 47 | 48 | -------------------------------------------------------------------------------- /yazi/plugins/clipboard.yazi/README.md: -------------------------------------------------------------------------------- 1 | # clipboard.yazi 2 | 3 | Synchronize the file paths currently yanked in the Yazi file manager with your system clipboard. The plugin supports macOS and Linux desktops out of the box. 4 | 5 | ## Requirements 6 | 7 | | Platform | Dependency | 8 | | -------- | ---------- | 9 | | Linux | `xclip` (X11), `wl-copy` (Wayland) | 10 | | macOS | Built-in `osascript` | 11 | 12 | ## Installation 13 | 14 | Install the plugin via the package manager: 15 | 16 | ```bash 17 | ya pkg add XYenon/clipboard 18 | ``` 19 | 20 | This clones the repository, adds it to `~/.config/yazi/package.toml`, and pins the current revision. 21 | 22 | ## Usage 23 | 24 | Add a shortcut in `~/.config/yazi/keymap.toml`: 25 | 26 | ```toml 27 | [[mgr.prepend_keymap]] 28 | on = "y" 29 | run = [ "yank", 'plugin clipboard -- --action=copy' ] 30 | ``` 31 | 32 | ## Optional arguments 33 | 34 | The plugin accepts the boolean argument `notify-unknown-display-server`: 35 | 36 | - Default `false`: silently exit when the Linux display server is unknown (useful for TTY or remote sessions). 37 | - `true`: show a notification to warn that copying is unavailable in the current session. 38 | 39 | Example invocation: 40 | 41 | ```toml 42 | [[mgr.prepend_keymap]] 43 | on = "y" 44 | run = [ "yank", 'plugin clipboard -- --action=copy --notify-unknown-display-server' ] 45 | ``` 46 | 47 | ## Troubleshooting 48 | 49 | - **`Copy failed: xclip/wl-copy not found`**: install `xclip` for X11 or `wl-clipboard` (`wl-copy`) for Wayland. 50 | - **`Unknown display server`**: ensure Yazi runs in a Wayland or X11 session. Enable `notify-unknown-display-server` to surface a visible warning. 51 | 52 | ## Development 53 | 54 | This repository uses [treefmt](https://github.com/numtide/treefmt) for formatting: 55 | 56 | ```bash 57 | nix fmt 58 | ``` 59 | 60 | Feel free to open a PR to support more desktop environments or add paste functionality. 61 | -------------------------------------------------------------------------------- /nvim/lua/config/lazy.lua: -------------------------------------------------------------------------------- 1 | local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" 2 | if not (vim.uv or vim.loop).fs_stat(lazypath) then 3 | local lazyrepo = "https://github.com/folke/lazy.nvim.git" 4 | local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) 5 | if vim.v.shell_error ~= 0 then 6 | vim.api.nvim_echo({ 7 | { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, 8 | { out, "WarningMsg" }, 9 | { "\nPress any key to exit..." }, 10 | }, true, {}) 11 | vim.fn.getchar() 12 | os.exit(1) 13 | end 14 | end 15 | vim.opt.rtp:prepend(lazypath) 16 | 17 | require("lazy").setup({ 18 | spec = { 19 | -- add LazyVim and import its plugins 20 | { "LazyVim/LazyVim", import = "lazyvim.plugins" }, 21 | -- import/override with your plugins 22 | { import = "plugins" }, 23 | }, 24 | defaults = { 25 | -- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup. 26 | -- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default. 27 | lazy = false, 28 | -- It's recommended to leave version=false for now, since a lot the plugin that support versioning, 29 | -- have outdated releases, which may break your Neovim install. 30 | version = false, -- always use the latest git commit 31 | -- version = "*", -- try installing the latest stable version for plugins that support semver 32 | }, 33 | install = { colorscheme = { "tokyonight", "habamax" } }, 34 | checker = { 35 | enabled = true, -- check for plugin updates periodically 36 | notify = false, -- notify on update 37 | }, -- automatically check for plugin updates 38 | performance = { 39 | rtp = { 40 | -- disable some rtp plugins 41 | disabled_plugins = { 42 | "gzip", 43 | -- "matchit", 44 | -- "matchparen", 45 | -- "netrwPlugin", 46 | "tarPlugin", 47 | "tohtml", 48 | "tutor", 49 | "zipPlugin", 50 | }, 51 | }, 52 | }, 53 | }) 54 | -------------------------------------------------------------------------------- /nvim/lua/config/options.lua: -------------------------------------------------------------------------------- 1 | -- Options are automatically loaded before lazy.nvim startup 2 | -- Default options that are always set: https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/config/options.lua 3 | -- Add any additional options here 4 | 5 | -- ============================================================================ 6 | -- FIX: Disable syntax concealment for markdown files 7 | -- ============================================================================ 8 | -- LazyVim sets conceallevel=2 which hides code fence delimiters (```). 9 | -- This makes it difficult to see where code blocks start/end. 10 | -- Setting conceallevel=0 shows all characters as-is. 11 | vim.api.nvim_create_autocmd("FileType", { 12 | pattern = { "markdown" }, 13 | callback = function() 14 | vim.opt_local.conceallevel = 0 15 | end, 16 | desc = "Disable conceal for markdown (show code fence backticks)", 17 | }) 18 | 19 | -- Font configuration for GUI clients (Neovide, VimR, etc.) 20 | vim.opt.guifont = "Berkeley Mono:h14" 21 | 22 | -- ============================================================================ 23 | -- FIX: Disable terminal focus reporting (DECSET 1004) 24 | -- ============================================================================ 25 | -- When nvim is spawned from claude-cli (Ctrl+G), it can re-enable focus 26 | -- reporting when exiting, causing escape sequences [[O and [[I to appear. 27 | -- This is a known bug in claude-code (issues #9137, #9218, #10375). 28 | -- These autocmds ensure focus reporting stays disabled. 29 | 30 | vim.api.nvim_create_autocmd({ "VimEnter", "VimResume" }, { 31 | callback = function() 32 | -- Write directly to the controlling terminal 33 | local tty = io.open("/dev/tty", "w") 34 | if tty then 35 | tty:write("\027[?1004l") 36 | tty:flush() 37 | tty:close() 38 | end 39 | end, 40 | desc = "Disable focus reporting on nvim start/resume", 41 | }) 42 | 43 | vim.api.nvim_create_autocmd({ "VimLeave", "VimSuspend" }, { 44 | callback = function() 45 | -- Write directly to the controlling terminal 46 | local tty = io.open("/dev/tty", "w") 47 | if tty then 48 | tty:write("\027[?1004l") 49 | tty:flush() 50 | tty:close() 51 | end 52 | end, 53 | desc = "Disable focus reporting on nvim exit/suspend", 54 | }) 55 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | tap "gromgit/brewtils" 2 | tap "homebrew/autoupdate" 3 | tap "nikitabobko/tap" 4 | tap "oven-sh/bun" 5 | brew "libtiff" 6 | brew "webp" 7 | brew "jpeg-xl" 8 | brew "aom" 9 | brew "glib" 10 | brew "at-spi2-core" 11 | brew "blueutil" 12 | brew "fastfetch" 13 | brew "fd" 14 | brew "unbound" 15 | brew "gnutls" 16 | brew "icu4c@78" 17 | brew "harfbuzz" 18 | brew "openjpeg" 19 | brew "leptonica" 20 | brew "libass" 21 | brew "pango" 22 | brew "tesseract" 23 | brew "ffmpeg" 24 | brew "fnm" 25 | brew "fzf" 26 | brew "gh" 27 | brew "ghostscript" 28 | brew "git" 29 | brew "git-lfs" 30 | brew "gitleaks" 31 | brew "gnupg" 32 | brew "gobject-introspection" 33 | brew "gpgme" 34 | brew "graphviz" 35 | brew "gsettings-desktop-schemas" 36 | brew "gtk+3" 37 | brew "httrack" 38 | brew "python@3.12" 39 | brew "libraw" 40 | brew "imagemagick" 41 | brew "jq" 42 | brew "lazygit" 43 | brew "libraqm" 44 | brew "llvm" 45 | brew "neovim" 46 | brew "node" 47 | brew "nushell" 48 | brew "pandoc" 49 | brew "parallel" 50 | brew "pillow" 51 | brew "poetry" 52 | brew "poppler" 53 | brew "postgresql@14" 54 | brew "pre-commit" 55 | brew "pyenv" 56 | brew "python@3.10" 57 | brew "python@3.11" 58 | brew "redis" 59 | brew "resvg" 60 | brew "ripgrep" 61 | brew "ruff" 62 | brew "sevenzip" 63 | brew "shellcheck" 64 | brew "terminal-notifier" 65 | brew "tmux" 66 | brew "tree" 67 | brew "uv" 68 | brew "vim" 69 | brew "wget" 70 | brew "yazi" 71 | brew "yt-dlp" 72 | brew "zoxide" 73 | brew "gromgit/brewtils/taproom" 74 | brew "oven-sh/bun/bun" 75 | cask "aerospace" 76 | cask "droid" 77 | cask "font-google-sans-code" 78 | cask "font-symbols-only-nerd-font" 79 | cask "gcloud-cli" 80 | cask "iterm2" 81 | cask "kitty" 82 | cask "miniconda" 83 | cask "mitmproxy" 84 | cask "ngrok" 85 | cask "wezterm" 86 | vscode "antfu.slidev" 87 | vscode "anthropic.claude-code" 88 | vscode "anysphere.cursorpyright" 89 | vscode "bedirt.gpt-token-counter-live" 90 | vscode "bungcip.better-toml" 91 | vscode "charliermarsh.ruff" 92 | vscode "dotjoshjohnson.xml" 93 | vscode "dvirtz.parquet-viewer" 94 | vscode "github.vscode-github-actions" 95 | vscode "janisdd.vscode-edit-csv" 96 | vscode "mechatroner.rainbow-csv" 97 | vscode "mermaidchart.vscode-mermaid-chart" 98 | vscode "monokai.theme-monokai-pro-vscode" 99 | vscode "ms-azuretools.vscode-docker" 100 | vscode "ms-playwright.playwright" 101 | vscode "ms-python.debugpy" 102 | vscode "ms-python.pylint" 103 | vscode "ms-python.python" 104 | vscode "ms-toolsai.jupyter" 105 | vscode "ms-toolsai.jupyter-renderers" 106 | vscode "ms-toolsai.vscode-jupyter-cell-tags" 107 | vscode "ms-toolsai.vscode-jupyter-slideshow" 108 | vscode "ms-vscode.live-server" 109 | vscode "nimda.deepdark-material" 110 | vscode "openai.chatgpt" 111 | vscode "phu1237.vs-browser" 112 | vscode "rust-lang.rust-analyzer" 113 | vscode "sst-dev.opencode" 114 | vscode "tauri-apps.tauri-vscode" 115 | vscode "tomoki1207.pdf" 116 | vscode "vscodevim.vim" 117 | vscode "zeshuaro.vscode-python-poetry" 118 | -------------------------------------------------------------------------------- /nvim/lazy-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "LazyVim": { "branch": "main", "commit": "d72127eb936f7f05d88d4fc316bc7e89080d69d8" }, 3 | "blink.cmp": { "branch": "main", "commit": "327fff91fe6af358e990be7be1ec8b78037d2138" }, 4 | "bufferline.nvim": { "branch": "main", "commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3" }, 5 | "catppuccin": { "branch": "main", "commit": "8c4125e3c746976ba025dc5d908fa22c6aa09486" }, 6 | "conform.nvim": { "branch": "master", "commit": "9fd3d5e0b689ec1bf400c53cbbec72c6fdf24081" }, 7 | "flash.nvim": { "branch": "main", "commit": "fcea7ff883235d9024dc41e638f164a450c14ca2" }, 8 | "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" }, 9 | "gitsigns.nvim": { "branch": "main", "commit": "20ad4419564d6e22b189f6738116b38871082332" }, 10 | "grug-far.nvim": { "branch": "main", "commit": "3e72397465f774b01aa38e4fe8e6eecf23d766d9" }, 11 | "lazy.nvim": { "branch": "main", "commit": "db067881fff0fd4be8c00e5bde7492e0e1c77a2f" }, 12 | "lazydev.nvim": { "branch": "main", "commit": "371cd7434cbf95606f1969c2c744da31b77fcfa6" }, 13 | "lualine.nvim": { "branch": "master", "commit": "3946f0122255bc377d14a59b27b609fb3ab25768" }, 14 | "mason-lspconfig.nvim": { "branch": "main", "commit": "d7b5feb6e769e995f7fcf44d92f49f811c51d10c" }, 15 | "mason.nvim": { "branch": "main", "commit": "ad7146aa61dcaeb54fa900144d768f040090bff0" }, 16 | "mini.ai": { "branch": "main", "commit": "11c57180bc9084089206e211ac7aa598bedc9673" }, 17 | "mini.icons": { "branch": "main", "commit": "284798619aed9f4c1ac1b9417b9a5e3b4b85ef3a" }, 18 | "mini.pairs": { "branch": "main", "commit": "b9aada8c0e59f2b938e98fbf4eae0799eba96ad9" }, 19 | "noice.nvim": { "branch": "main", "commit": "5099348591f7d3ba9e547b1e631c694c65bbe0b9" }, 20 | "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, 21 | "nvim-lint": { "branch": "master", "commit": "91764de033247e9a52c4ee01d52a0ac1cf1ed037" }, 22 | "nvim-lspconfig": { "branch": "master", "commit": "a89bfcfd0e44f898341ac8a80ba83ccf6218bef3" }, 23 | "nvim-treesitter": { "branch": "main", "commit": "64f4755b9d6ea9008265b09eb79fd91727311682" }, 24 | "nvim-treesitter-textobjects": { "branch": "main", "commit": "1f29f2f788dbbf6573a0d968a6dfe75a0fa628d0" }, 25 | "nvim-ts-autotag": { "branch": "main", "commit": "c4ca798ab95b316a768d51eaaaee48f64a4a46bc" }, 26 | "persistence.nvim": { "branch": "main", "commit": "b20b2a7887bd39c1a356980b45e03250f3dce49c" }, 27 | "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, 28 | "snacks.nvim": { "branch": "main", "commit": "9ad41782eced6a06034e568357cdad35cbf52ffa" }, 29 | "todo-comments.nvim": { "branch": "main", "commit": "411503d3bedeff88484de572f2509c248e499b38" }, 30 | "tokyonight.nvim": { "branch": "main", "commit": "2642dbb83333e0575d1c3436e1d837926871c5fb" }, 31 | "trouble.nvim": { "branch": "main", "commit": "bd67efe408d4816e25e8491cc5ad4088e708a69a" }, 32 | "ts-comments.nvim": { "branch": "main", "commit": "123a9fb12e7229342f807ec9e6de478b1102b041" }, 33 | "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" } 34 | } 35 | -------------------------------------------------------------------------------- /ghostty/config: -------------------------------------------------------------------------------- 1 | # Ghostty Configuration 2 | # Native Ghostty Tabs Configuration 3 | 4 | # ============================================ 5 | # THEME - Auto-switches with macOS appearance 6 | # ============================================ 7 | theme = dark:Catppuccin Macchiato,light:Catppuccin Latte 8 | 9 | # ============================================ 10 | # WINDOW PADDING 11 | # ============================================ 12 | window-padding-x = 20 13 | window-padding-y = 20 14 | 15 | # ============================================ 16 | # TAB BAR VISIBILITY 17 | # ============================================ 18 | window-show-tab-bar = always 19 | 20 | # ============================================ 21 | # TAB MANAGEMENT 22 | # ============================================ 23 | keybind = super+t=new_tab 24 | keybind = super+w=close_surface 25 | keybind = super+shift+bracket_left=previous_tab 26 | keybind = super+shift+bracket_right=next_tab 27 | 28 | # ============================================ 29 | # TAB SELECTION BY NUMBER 30 | # ============================================ 31 | keybind = super+1=goto_tab:1 32 | keybind = super+2=goto_tab:2 33 | keybind = super+3=goto_tab:3 34 | keybind = super+4=goto_tab:4 35 | keybind = super+5=goto_tab:5 36 | keybind = super+6=goto_tab:6 37 | keybind = super+7=goto_tab:7 38 | keybind = super+8=goto_tab:8 39 | keybind = super+9=last_tab 40 | 41 | # ============================================ 42 | # SPLITS - Direct Navigation (super+hjkl) 43 | # ============================================ 44 | keybind = super+k=unbind 45 | keybind = super+h=goto_split:left 46 | keybind = super+j=goto_split:down 47 | keybind = super+k=goto_split:up 48 | keybind = super+l=goto_split:right 49 | 50 | # ============================================ 51 | # SPLITS - Leader Navigation (super+space > hjkl) 52 | # ============================================ 53 | keybind = super+space>h=goto_split:left 54 | keybind = super+space>j=goto_split:down 55 | keybind = super+space>k=goto_split:up 56 | keybind = super+space>l=goto_split:right 57 | 58 | # ============================================ 59 | # SPLITS - Resizing (super+space > shift+hjkl) 60 | # ============================================ 61 | keybind = super+space>shift+h=resize_split:left,50 62 | keybind = super+space>shift+j=resize_split:down,50 63 | keybind = super+space>shift+k=resize_split:up,50 64 | keybind = super+space>shift+l=resize_split:right,50 65 | 66 | # ============================================ 67 | # SPLITS - Creation 68 | # ============================================ 69 | keybind = super+space>s=new_split:down 70 | keybind = super+space>v=new_split:right 71 | keybind = super+space>minus=new_split:down 72 | keybind = super+space>shift+backslash=new_split:right 73 | 74 | # ============================================ 75 | # SPLITS - Management 76 | # ============================================ 77 | keybind = super+space>x=close_surface 78 | keybind = super+space>q=close_surface 79 | keybind = super+space>z=toggle_split_zoom 80 | keybind = super+space>bracket_left=copy_to_clipboard 81 | 82 | # ============================================ 83 | # macOS Settings 84 | # ============================================ 85 | macos-titlebar-style = tabs 86 | clipboard-paste-protection = false 87 | -------------------------------------------------------------------------------- /scripts/sync-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ============================================================================= 3 | # sync-status.sh - Check dotfiles sync health 4 | # ============================================================================= 5 | # Usage: 6 | # ./sync-status.sh # Full status 7 | # ./sync-status.sh --brief # One-line summary (for shell prompt) 8 | # ./sync-status.sh --check # Exit 1 if stale (for shell startup warning) 9 | # ============================================================================= 10 | 11 | DOTFILES_DIR="$HOME/.dotfiles" 12 | HEALTH_FILE="$DOTFILES_DIR/.last-sync" 13 | STALE_HOURS=24 # Consider stale after 24 hours 14 | 15 | # Colors 16 | RED='\033[0;31m' 17 | GREEN='\033[0;32m' 18 | YELLOW='\033[0;33m' 19 | NC='\033[0m' # No Color 20 | 21 | get_status() { 22 | if [[ ! -f "$HEALTH_FILE" ]]; then 23 | echo "unknown|never|No sync recorded" 24 | return 25 | fi 26 | cat "$HEALTH_FILE" 27 | } 28 | 29 | is_stale() { 30 | if [[ ! -f "$HEALTH_FILE" ]]; then 31 | return 0 # No file = stale 32 | fi 33 | 34 | local last_sync 35 | last_sync=$(stat -f %m "$HEALTH_FILE" 2>/dev/null || echo 0) 36 | local now 37 | now=$(date +%s) 38 | local age_hours=$(( (now - last_sync) / 3600 )) 39 | 40 | [[ $age_hours -ge $STALE_HOURS ]] 41 | } 42 | 43 | show_full_status() { 44 | echo "=== Dotfiles Sync Status ===" 45 | echo "" 46 | 47 | local status_line 48 | status_line=$(get_status) 49 | local status 50 | status=$(echo "$status_line" | cut -d'|' -f1) 51 | local timestamp 52 | timestamp=$(echo "$status_line" | cut -d'|' -f2) 53 | local message 54 | message=$(echo "$status_line" | cut -d'|' -f3-) 55 | 56 | case "$status" in 57 | ok) 58 | echo -e "Status: ${GREEN}OK${NC}" 59 | ;; 60 | error) 61 | echo -e "Status: ${RED}ERROR${NC}" 62 | ;; 63 | *) 64 | echo -e "Status: ${YELLOW}UNKNOWN${NC}" 65 | ;; 66 | esac 67 | 68 | echo "Last sync: $timestamp" 69 | echo "Message: $message" 70 | echo "" 71 | 72 | # Check if stale 73 | if is_stale; then 74 | echo -e "${YELLOW}Warning: Sync is stale (>$STALE_HOURS hours)${NC}" 75 | fi 76 | 77 | # Show pending changes 78 | cd "$DOTFILES_DIR" 2>/dev/null || return 79 | local changes 80 | changes=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') 81 | if [[ $changes -gt 0 ]]; then 82 | echo "" 83 | echo "Pending changes: $changes file(s)" 84 | git status --short 85 | fi 86 | } 87 | 88 | show_brief() { 89 | local status_line 90 | status_line=$(get_status) 91 | local status 92 | status=$(echo "$status_line" | cut -d'|' -f1) 93 | local timestamp 94 | timestamp=$(echo "$status_line" | cut -d'|' -f2) 95 | 96 | case "$status" in 97 | ok) 98 | if is_stale; then 99 | echo -e "${YELLOW}dotfiles: stale${NC}" 100 | else 101 | echo -e "${GREEN}dotfiles: ok${NC}" 102 | fi 103 | ;; 104 | error) 105 | echo -e "${RED}dotfiles: error${NC}" 106 | ;; 107 | *) 108 | echo -e "${YELLOW}dotfiles: unknown${NC}" 109 | ;; 110 | esac 111 | } 112 | 113 | check_health() { 114 | local status_line 115 | status_line=$(get_status) 116 | local status 117 | status=$(echo "$status_line" | cut -d'|' -f1) 118 | 119 | if [[ "$status" == "error" ]]; then 120 | echo -e "${RED}Dotfiles sync failed! Run: dfs${NC}" 121 | return 1 122 | fi 123 | 124 | if is_stale; then 125 | echo -e "${YELLOW}Dotfiles sync stale (>$STALE_HOURS hours). Run: ~/.dotfiles/scripts/auto-sync.sh${NC}" 126 | return 1 127 | fi 128 | 129 | return 0 130 | } 131 | 132 | case "${1:-}" in 133 | --brief) 134 | show_brief 135 | ;; 136 | --check) 137 | check_health 138 | ;; 139 | *) 140 | show_full_status 141 | ;; 142 | esac 143 | -------------------------------------------------------------------------------- /nvim/lua/plugins/snacks-explorer.lua: -------------------------------------------------------------------------------- 1 | -- Snacks Explorer Configuration with Yazi-inspired keybindings 2 | -- This configures the file explorer to match Yazi's navigation patterns 3 | -- while avoiding conflicts with LazyVim's global keybindings 4 | 5 | return { 6 | { 7 | "folke/snacks.nvim", 8 | opts = { 9 | picker = { 10 | sources = { 11 | explorer = { 12 | -- Explorer window configuration 13 | win = { 14 | list = { 15 | keys = { 16 | -- ============================================================ 17 | -- SELECTION (Yazi-aligned) 18 | -- ============================================================ 19 | -- Use Space for toggling selection (Yazi-style) 20 | -- Note: Tab is the default, we're changing it to Space 21 | [""] = "", -- Maps Space to the default toggle action 22 | 23 | -- ============================================================ 24 | -- FILE OPERATIONS (Yazi-aligned) 25 | -- ============================================================ 26 | -- Add 'y' as alias for copy (Yazi uses 'y' for yank/copy) 27 | ["y"] = "c", -- Map 'y' to copy action (default is 'c') 28 | 29 | -- Add 'x' for cut (Yazi-style) 30 | -- Note: Snacks uses 'm' for move, which is similar to cut 31 | ["x"] = "m", -- Map 'x' to move action 32 | 33 | -- Y - Copy file path to clipboard (custom action) 34 | ["Y"] = "copy_path", 35 | 36 | -- Keep all other default bindings as they align with Yazi: 37 | -- c - copy (keep as alternative to 'y') 38 | -- p - paste (already aligned) 39 | -- d - delete (already aligned) 40 | -- r - rename (already aligned) 41 | -- a - add/create (already aligned) 42 | -- o - open with system (already aligned) 43 | -- l/ - open file or enter directory (already aligned) 44 | -- h - close directory (already aligned) 45 | -- - go to parent directory (already aligned) 46 | 47 | -- ============================================================ 48 | -- TOGGLE FEATURES (Keep Snacks defaults) 49 | -- ============================================================ 50 | -- H - toggle hidden (already aligned with Yazi concept) 51 | -- I - toggle ignored files 52 | -- P - toggle preview 53 | -- Z - close all directories 54 | 55 | -- ============================================================ 56 | -- OTHER ALIGNMENTS 57 | -- ============================================================ 58 | -- u - refresh (Snacks default, close to Yazi's concept) 59 | -- / - search (already aligned) 60 | -- . - set as cwd (Snacks default, different from Yazi but useful) 61 | 62 | -- ============================================================ 63 | -- Note: Advanced navigation (gg, G, , , etc.) 64 | -- These work by default in Vim/Neovim for list navigation 65 | -- ============================================================ 66 | }, 67 | }, 68 | }, 69 | -- Custom actions 70 | actions = { 71 | copy_path = { 72 | action = function(picker, item) 73 | -- Get the file path 74 | local path = item.file 75 | if path then 76 | -- Copy to system clipboard 77 | vim.fn.setreg("+", path) 78 | -- Also copy to unnamed register 79 | vim.fn.setreg('"', path) 80 | -- Show notification 81 | Snacks.notify.info("Copied path: " .. path) 82 | end 83 | end, 84 | }, 85 | }, 86 | }, 87 | }, 88 | }, 89 | }, 90 | }, 91 | } 92 | -------------------------------------------------------------------------------- /scripts/auto-sync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ============================================================================= 3 | # auto-sync.sh - Automated dotfiles sync with secret protection 4 | # ============================================================================= 5 | # Features: 6 | # - Secret scanning via gitleaks before commit 7 | # - macOS notifications on failure 8 | # - Health file tracking for monitoring 9 | # - Brewfile auto-update 10 | # 11 | # Usage: 12 | # ./auto-sync.sh # Run sync 13 | # ./auto-sync.sh --force # Skip secret scan (not recommended) 14 | # ============================================================================= 15 | 16 | set -euo pipefail 17 | 18 | DOTFILES_DIR="$HOME/.dotfiles" 19 | HEALTH_FILE="$DOTFILES_DIR/.last-sync" 20 | LOG_FILE="$DOTFILES_DIR/.sync.log" 21 | LOCK_FILE="/tmp/dotfiles-sync.lock" 22 | 23 | # ============================================================================= 24 | # HELPERS 25 | # ============================================================================= 26 | 27 | log() { 28 | local timestamp 29 | timestamp=$(date '+%Y-%m-%d %H:%M:%S') 30 | echo "[$timestamp] $1" | tee -a "$LOG_FILE" 31 | } 32 | 33 | notify_error() { 34 | local message="$1" 35 | log "ERROR: $message" 36 | 37 | # macOS notification 38 | osascript -e "display notification \"$message\" with title \"Dotfiles Sync Failed\" sound name \"Basso\"" 2>/dev/null || true 39 | 40 | # Update health file with error 41 | echo "error|$(date '+%Y-%m-%d %H:%M:%S')|$message" > "$HEALTH_FILE" 42 | } 43 | 44 | notify_success() { 45 | local message="$1" 46 | log "SUCCESS: $message" 47 | 48 | # Update health file 49 | echo "ok|$(date '+%Y-%m-%d %H:%M:%S')|$message" > "$HEALTH_FILE" 50 | } 51 | 52 | cleanup() { 53 | rm -f "$LOCK_FILE" 54 | } 55 | 56 | # ============================================================================= 57 | # MAIN 58 | # ============================================================================= 59 | 60 | main() { 61 | # Parse args 62 | local skip_scan=false 63 | if [[ "${1:-}" == "--force" ]]; then 64 | skip_scan=true 65 | log "WARNING: Skipping secret scan (--force)" 66 | fi 67 | 68 | # Prevent concurrent runs 69 | if [[ -f "$LOCK_FILE" ]]; then 70 | pid=$(cat "$LOCK_FILE" 2>/dev/null || echo "") 71 | if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then 72 | log "Another sync is running (PID: $pid). Exiting." 73 | exit 0 74 | fi 75 | rm -f "$LOCK_FILE" 76 | fi 77 | echo $$ > "$LOCK_FILE" 78 | trap cleanup EXIT 79 | 80 | cd "$DOTFILES_DIR" || { 81 | notify_error "Cannot access $DOTFILES_DIR" 82 | exit 1 83 | } 84 | 85 | log "Starting dotfiles sync..." 86 | 87 | # Update Brewfile 88 | if command -v brew &>/dev/null; then 89 | log "Updating Brewfile..." 90 | if ! brew bundle dump --file="$DOTFILES_DIR/Brewfile" --force 2>&1 | tee -a "$LOG_FILE"; then 91 | log "WARNING: Brewfile update failed, continuing..." 92 | fi 93 | fi 94 | 95 | # Check for changes 96 | if git diff --quiet && git diff --cached --quiet && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then 97 | log "No changes to sync" 98 | notify_success "No changes" 99 | exit 0 100 | fi 101 | 102 | # Run gitleaks scan before staging 103 | if [[ "$skip_scan" == "false" ]]; then 104 | log "Scanning for secrets..." 105 | if ! gitleaks detect --source . --verbose 2>&1 | tee -a "$LOG_FILE"; then 106 | notify_error "Secrets detected! Commit blocked." 107 | exit 1 108 | fi 109 | fi 110 | 111 | # Stage all changes 112 | git add -A 113 | 114 | # Run pre-commit hooks (includes gitleaks) 115 | if [[ "$skip_scan" == "false" ]] && command -v pre-commit &>/dev/null; then 116 | log "Running pre-commit hooks..." 117 | if ! pre-commit run --all-files 2>&1 | tee -a "$LOG_FILE"; then 118 | notify_error "Pre-commit hooks failed!" 119 | git reset HEAD 120 | exit 1 121 | fi 122 | fi 123 | 124 | # Commit 125 | local commit_msg 126 | commit_msg="Auto-sync: $(date '+%Y-%m-%d %H:%M')" 127 | if ! git commit -m "$commit_msg" 2>&1 | tee -a "$LOG_FILE"; then 128 | notify_error "Commit failed!" 129 | exit 1 130 | fi 131 | 132 | # Push 133 | log "Pushing to remote..." 134 | if ! git push 2>&1 | tee -a "$LOG_FILE"; then 135 | notify_error "Push failed! Check network." 136 | exit 1 137 | fi 138 | 139 | notify_success "Synced $(git rev-parse --short HEAD)" 140 | log "Sync complete!" 141 | } 142 | 143 | main "$@" 144 | -------------------------------------------------------------------------------- /yazi/plugins/clipboard.yazi/main.lua: -------------------------------------------------------------------------------- 1 | local get_yanked_paths = ya.sync(function(state) 2 | local paths = {} 3 | for _, v in pairs(cx.yanked) do 4 | if not v.is_regular then 5 | goto continue 6 | end 7 | table.insert(paths, tostring(v)) 8 | ::continue:: 9 | end 10 | return paths 11 | end) 12 | 13 | local M = { 14 | notify_unknown_display_server = false, 15 | } 16 | 17 | function M:entry(job) 18 | ya.dbg("Clipboard", "args", job.args) 19 | self.notify_unknown_display_server = job.args.notify_unknown_display_server or false 20 | 21 | if job.args.action == "copy" then 22 | return self:copy() 23 | elseif job.args.action == "paste" then 24 | return self:notify_error("Paste action is not implemented yet") 25 | else 26 | return self:notify_error("Unknown action: " .. tostring(job.args.action)) 27 | end 28 | end 29 | 30 | function M:copy() 31 | local paths = get_yanked_paths() 32 | ya.dbg("Clipboard", "files", paths) 33 | if #paths == 0 then 34 | return self:notify_error("No files to copy") 35 | end 36 | 37 | local cmd, err = nil, nil 38 | if ya.target_os() == "linux" then 39 | cmd, err = self:copy_linux_cmd() 40 | elseif ya.target_os() == "macos" then 41 | cmd, err = self:copy_macos_cmd() 42 | else 43 | err = "Unsupported OS: " .. ya.target_os() 44 | end 45 | if not self.notify_unknown_display_server and err == "Unknown display server" then 46 | return 47 | end 48 | if err then 49 | return self:notify_error("Copy failed: " .. err) 50 | end 51 | 52 | ya.dbg("Clipboard", "cmd", cmd) 53 | local cmd = Command("sh"):arg({ "-c", cmd, "--" }):arg(paths) 54 | local output, err = cmd:output() 55 | if err then 56 | ya.err("Clipboard", "cmd failed", err) 57 | return self:notify_error("Run command failed: " .. tostring(err)) 58 | end 59 | if output then 60 | ya.dbg("Clipboard", "cmd output", output.status.code, output.stdout, output.stderr) 61 | end 62 | end 63 | 64 | function M:copy_linux_cmd() 65 | if self:linux_display_server() == "x11" then 66 | return self:copy_x11_cmd() 67 | elseif self:linux_display_server() == "wayland" then 68 | return self:copy_wayland_cmd() 69 | else 70 | return nil, "Unknown display server" 71 | end 72 | end 73 | 74 | function M:copy_macos_cmd() 75 | -- Generated by GPT-5-Codex 76 | cmd = [[osascript - "$@" < /dev/null; then 69 | echo "Installing Homebrew packages..." 70 | brew bundle --file="$DOTFILES_DIR/Brewfile" 71 | 72 | # Install secret protection tools 73 | echo "Installing secret protection tools..." 74 | brew install gitleaks pre-commit 2>/dev/null || true 75 | fi 76 | 77 | # ============================================================================= 78 | # PRE-COMMIT HOOKS 79 | # ============================================================================= 80 | 81 | if command -v pre-commit &> /dev/null; then 82 | echo "Installing pre-commit hooks..." 83 | cd "$DOTFILES_DIR" 84 | pre-commit install 85 | fi 86 | 87 | # ============================================================================= 88 | # SECRETS TEMPLATE 89 | # ============================================================================= 90 | 91 | if [[ ! -f ~/.secrets.env ]]; then 92 | echo "Creating secrets template..." 93 | cp "$DOTFILES_DIR/secrets.env.template" ~/.secrets.env 94 | echo "Edit ~/.secrets.env to add your API keys (gitignored, safe)" 95 | fi 96 | 97 | # ============================================================================= 98 | # AUTO-SYNC (LAUNCHD) 99 | # ============================================================================= 100 | 101 | if [[ "$SKIP_SYNC" == "false" ]] && [[ "$(uname)" == "Darwin" ]]; then 102 | echo "Setting up auto-sync..." 103 | 104 | PLIST_NAME="com.dotfiles.autosync.plist" 105 | PLIST_SRC="$DOTFILES_DIR/scripts/$PLIST_NAME" 106 | PLIST_DST="$HOME/Library/LaunchAgents/$PLIST_NAME" 107 | 108 | # Unload if already loaded 109 | launchctl unload "$PLIST_DST" 2>/dev/null || true 110 | 111 | # Expand $HOME in plist and install 112 | mkdir -p ~/Library/LaunchAgents 113 | sed "s|\$HOME|$HOME|g" "$PLIST_SRC" > "$PLIST_DST" 114 | 115 | # Load the agent 116 | launchctl load "$PLIST_DST" 117 | 118 | echo "Auto-sync enabled (runs hourly)." 119 | fi 120 | 121 | # ============================================================================= 122 | # DONE 123 | # ============================================================================= 124 | 125 | echo "Dotfiles installed successfully!" 126 | echo "" 127 | echo "Next steps:" 128 | echo " 1. Edit ~/.secrets.env with your API keys" 129 | echo " 2. Open a new terminal and run 'dfs' to check sync status" 130 | -------------------------------------------------------------------------------- /nvim/lua/plugins/example.lua: -------------------------------------------------------------------------------- 1 | -- since this is just an example spec, don't actually load anything here and return an empty spec 2 | -- stylua: ignore 3 | if true then return {} end 4 | 5 | -- every spec file under the "plugins" directory will be loaded automatically by lazy.nvim 6 | -- 7 | -- In your plugin files, you can: 8 | -- * add extra plugins 9 | -- * disable/enabled LazyVim plugins 10 | -- * override the configuration of LazyVim plugins 11 | return { 12 | -- add gruvbox 13 | { "ellisonleao/gruvbox.nvim" }, 14 | 15 | -- Configure LazyVim to load gruvbox 16 | { 17 | "LazyVim/LazyVim", 18 | opts = { 19 | colorscheme = "gruvbox", 20 | }, 21 | }, 22 | 23 | -- change trouble config 24 | { 25 | "folke/trouble.nvim", 26 | -- opts will be merged with the parent spec 27 | opts = { use_diagnostic_signs = true }, 28 | }, 29 | 30 | -- disable trouble 31 | { "folke/trouble.nvim", enabled = false }, 32 | 33 | -- override nvim-cmp and add cmp-emoji 34 | { 35 | "hrsh7th/nvim-cmp", 36 | dependencies = { "hrsh7th/cmp-emoji" }, 37 | ---@param opts cmp.ConfigSchema 38 | opts = function(_, opts) 39 | table.insert(opts.sources, { name = "emoji" }) 40 | end, 41 | }, 42 | 43 | -- change some telescope options and a keymap to browse plugin files 44 | { 45 | "nvim-telescope/telescope.nvim", 46 | keys = { 47 | -- add a keymap to browse plugin files 48 | -- stylua: ignore 49 | { 50 | "fp", 51 | function() require("telescope.builtin").find_files({ cwd = require("lazy.core.config").options.root }) end, 52 | desc = "Find Plugin File", 53 | }, 54 | }, 55 | -- change some options 56 | opts = { 57 | defaults = { 58 | layout_strategy = "horizontal", 59 | layout_config = { prompt_position = "top" }, 60 | sorting_strategy = "ascending", 61 | winblend = 0, 62 | }, 63 | }, 64 | }, 65 | 66 | -- add pyright to lspconfig 67 | { 68 | "neovim/nvim-lspconfig", 69 | ---@class PluginLspOpts 70 | opts = { 71 | ---@type lspconfig.options 72 | servers = { 73 | -- pyright will be automatically installed with mason and loaded with lspconfig 74 | pyright = {}, 75 | }, 76 | }, 77 | }, 78 | 79 | -- add tsserver and setup with typescript.nvim instead of lspconfig 80 | { 81 | "neovim/nvim-lspconfig", 82 | dependencies = { 83 | "jose-elias-alvarez/typescript.nvim", 84 | init = function() 85 | require("lazyvim.util").lsp.on_attach(function(_, buffer) 86 | -- stylua: ignore 87 | vim.keymap.set( "n", "co", "TypescriptOrganizeImports", { buffer = buffer, desc = "Organize Imports" }) 88 | vim.keymap.set("n", "cR", "TypescriptRenameFile", { desc = "Rename File", buffer = buffer }) 89 | end) 90 | end, 91 | }, 92 | ---@class PluginLspOpts 93 | opts = { 94 | ---@type lspconfig.options 95 | servers = { 96 | -- tsserver will be automatically installed with mason and loaded with lspconfig 97 | tsserver = {}, 98 | }, 99 | -- you can do any additional lsp server setup here 100 | -- return true if you don't want this server to be setup with lspconfig 101 | ---@type table 102 | setup = { 103 | -- example to setup with typescript.nvim 104 | tsserver = function(_, opts) 105 | require("typescript").setup({ server = opts }) 106 | return true 107 | end, 108 | -- Specify * to use this function as a fallback for any server 109 | -- ["*"] = function(server, opts) end, 110 | }, 111 | }, 112 | }, 113 | 114 | -- for typescript, LazyVim also includes extra specs to properly setup lspconfig, 115 | -- treesitter, mason and typescript.nvim. So instead of the above, you can use: 116 | { import = "lazyvim.plugins.extras.lang.typescript" }, 117 | 118 | -- add more treesitter parsers 119 | { 120 | "nvim-treesitter/nvim-treesitter", 121 | opts = { 122 | ensure_installed = { 123 | "bash", 124 | "html", 125 | "javascript", 126 | "json", 127 | "lua", 128 | "markdown", 129 | "markdown_inline", 130 | "python", 131 | "query", 132 | "regex", 133 | "tsx", 134 | "typescript", 135 | "vim", 136 | "yaml", 137 | }, 138 | }, 139 | }, 140 | 141 | -- since `vim.tbl_deep_extend`, can only merge tables and not lists, the code above 142 | -- would overwrite `ensure_installed` with the new value. 143 | -- If you'd rather extend the default config, use the code below instead: 144 | { 145 | "nvim-treesitter/nvim-treesitter", 146 | opts = function(_, opts) 147 | -- add tsx and treesitter 148 | vim.list_extend(opts.ensure_installed, { 149 | "tsx", 150 | "typescript", 151 | }) 152 | end, 153 | }, 154 | 155 | -- the opts function can also be used to change the default opts: 156 | { 157 | "nvim-lualine/lualine.nvim", 158 | event = "VeryLazy", 159 | opts = function(_, opts) 160 | table.insert(opts.sections.lualine_x, { 161 | function() 162 | return "😄" 163 | end, 164 | }) 165 | end, 166 | }, 167 | 168 | -- or you can return new options to override all the defaults 169 | { 170 | "nvim-lualine/lualine.nvim", 171 | event = "VeryLazy", 172 | opts = function() 173 | return { 174 | --[[add your custom lualine config here]] 175 | } 176 | end, 177 | }, 178 | 179 | -- use mini.starter instead of alpha 180 | { import = "lazyvim.plugins.extras.ui.mini-starter" }, 181 | 182 | -- add jsonls and schemastore packages, and setup treesitter for json, json5 and jsonc 183 | { import = "lazyvim.plugins.extras.lang.json" }, 184 | 185 | -- add any tools you want to have installed below 186 | { 187 | "williamboman/mason.nvim", 188 | opts = { 189 | ensure_installed = { 190 | "stylua", 191 | "shellcheck", 192 | "shfmt", 193 | "flake8", 194 | }, 195 | }, 196 | }, 197 | } 198 | -------------------------------------------------------------------------------- /zsh/.zshrc: -------------------------------------------------------------------------------- 1 | # IMPORTANT: Core PATH configuration moved to ~/.zshenv 2 | # 3 | # ~/.zshenv is loaded for ALL shells (interactive + non-interactive) 4 | # This fixes pytest-xdist workers not finding docker/python. 5 | # See: ~/.zshenv for /usr/local/bin, Python 3.11, etc. 6 | 7 | 8 | # Add other paths correctly 9 | export PATH="/Users/alex/Library/Application Support/pypoetry/bin:$PATH" 10 | export PATH="/opt/homebrew/opt/poppler/bin:$PATH" 11 | export PATH="$PATH:$HOME/.local/opt/go/bin" 12 | export PATH="$PATH:$HOME/go/bin" 13 | 14 | # Add Homebrew to PATH 15 | export PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH" 16 | 17 | # Load vi mode key bindings (official zsh vi mode) 18 | bindkey -v 19 | 20 | # Reduce ESC delay (vi mode responsiveness) 21 | # Default is 40 (0.4s), setting to 10 (0.1s) for responsive but reliable vi mode 22 | export KEYTIMEOUT=10 23 | 24 | # Keep useful emacs-style keybindings in INSERT mode (viins keymap) 25 | bindkey -M viins '^R' history-incremental-search-backward # Ctrl+R for history search 26 | bindkey -M viins '^A' beginning-of-line # Ctrl+A for start of line 27 | bindkey -M viins '^E' end-of-line # Ctrl+E for end of line 28 | 29 | # Alt/Option + arrow keys in INSERT mode 30 | bindkey -M viins '^[[1;3D' backward-word # Option + Left 31 | bindkey -M viins '^[[1;3C' forward-word # Option + Right 32 | 33 | # Ensure terminal is set correctly 34 | export TERM=xterm-256color 35 | 36 | # Set default editor to Neovim (using wrapper to fix claude-code focus reporting bug) 37 | # See: https://github.com/anthropics/claude-code/issues/10375 38 | export EDITOR="$HOME/.local/bin/nvim-wrapper" 39 | export VISUAL="$HOME/.local/bin/nvim-wrapper" 40 | 41 | # Generated for envman. Do not edit. 42 | [ -s "$HOME/.config/envman/load.sh" ] && source "$HOME/.config/envman/load.sh" 43 | # opencode 44 | export PATH=/Users/alex/.opencode/bin:$PATH 45 | export PATH="$HOME/.local/bin:$PATH" 46 | 47 | # CC-Trace: mitmproxy configuration for Claude Code API interception 48 | proxy_claude() { 49 | # Set proxy environment variables 50 | export HTTP_PROXY=http://127.0.0.1:8080 51 | export HTTPS_PROXY=http://127.0.0.1:8080 52 | export http_proxy=http://127.0.0.1:8080 53 | export https_proxy=http://127.0.0.1:8080 54 | 55 | # Point Node.js to mitmproxy's CA certificate 56 | export NODE_EXTRA_CA_CERTS="$HOME/.mitmproxy/mitmproxy-ca-cert.pem" 57 | 58 | # Disable SSL verification warnings (use with caution - local debugging only) 59 | export NODE_TLS_REJECT_UNAUTHORIZED=0 60 | 61 | echo "🔍 Proxy configured for mitmproxy (http://127.0.0.1:8080)" 62 | echo "📜 Using CA cert: $NODE_EXTRA_CA_CERTS" 63 | echo "🚀 Starting Claude Code..." 64 | 65 | # Launch Claude Code (use cc wrapper with any passed flags) 66 | cc "$@" 67 | } 68 | 69 | # Claude CLI wrapper (cc) with custom short flags and project shortcuts 70 | # Expands short flags before passing to claude, disables focus reporting 71 | # 72 | # Custom flags (not native): 73 | # -dsp → --dangerously-skip-permissions 74 | # -mo → --model opus 75 | # -ms → --model sonnet 76 | # -mh → --model haiku 77 | # 78 | # Project shortcuts (dynamic): 79 | # @ → Opens session in ~/Documents/GitHub/ 80 | # Examples: @incide, @incide-portable, @cc-trace-new 81 | # 82 | # Native flags (pass through): 83 | # -r → --resume 84 | # -c → --continue 85 | # -p → --print 86 | # 87 | # Examples: 88 | # cc -dsp -mo → claude --dangerously-skip-permissions --model opus 89 | # cc -ms -r → claude --model sonnet --resume 90 | # cc -dsp -mh -c → claude --dangerously-skip-permissions --model haiku --continue 91 | # cc @incide → opens claude in ~/Documents/GitHub/incide 92 | # cc -dsp -mo @incide → skip permissions + opus in incide project 93 | # 94 | cc() { 95 | local dir="" 96 | local args=() 97 | local github_base="/Users/alex/Documents/GitHub" 98 | 99 | for arg in "$@"; do 100 | # @project pattern: opens Claude in ~/Documents/GitHub/ 101 | if [[ "$arg" == @* ]] && [[ ${#arg} -gt 1 ]]; then 102 | local project="${arg#@}" 103 | local project_path="$github_base/$project" 104 | if [[ -d "$project_path" ]]; then 105 | if [[ -n "$dir" ]]; then 106 | echo "cc: multiple projects specified (already using ${dir##*/})" >&2 107 | return 1 108 | fi 109 | dir="$project_path" 110 | else 111 | echo "cc: project '$project' not found in $github_base" >&2 112 | return 1 113 | fi 114 | elif [[ "$arg" == "-dsp" ]]; then 115 | args+=(--dangerously-skip-permissions) 116 | elif [[ "$arg" == "-mo" ]]; then 117 | args+=(--model opus) 118 | elif [[ "$arg" == "-ms" ]]; then 119 | args+=(--model sonnet) 120 | elif [[ "$arg" == "-mh" ]]; then 121 | args+=(--model haiku) 122 | else 123 | args+=("$arg") 124 | fi 125 | done 126 | 127 | printf '\e[?1004l' # Disable focus reporting before starting 128 | if [[ -n "$dir" ]]; then 129 | (cd "$dir" && command claude "${args[@]}") 130 | else 131 | command claude "${args[@]}" 132 | fi 133 | local exit_code=$? 134 | printf '\e[?1004l' # Re-disable after exit 135 | return $exit_code 136 | } 137 | 138 | # Tab completion for cc: completes @project names from GitHub directory 139 | _cc_complete() { 140 | local github_base="/Users/alex/Documents/GitHub" 141 | local cur="${words[CURRENT]}" 142 | 143 | # Complete @project shortcuts 144 | if [[ "$cur" == @* ]]; then 145 | local prefix="${cur#@}" 146 | local projects=("$github_base"/${prefix}*(/:t)) 147 | compadd -P '@' -- "${projects[@]}" 148 | # Complete flags 149 | elif [[ "$cur" == -* ]]; then 150 | compadd -- -dsp -mo -ms -mh -r -c -p 151 | fi 152 | } 153 | # Register completion if zsh completion system is available 154 | (( $+functions[compdef] )) && compdef _cc_complete cc 155 | 156 | # Added by Antigravity 157 | export PATH="/Users/alex/.antigravity/antigravity/bin:$PATH" 158 | source "$HOME/.cargo/env" 159 | 160 | # ============================================================================ 161 | # FIX: Disable focus reporting (MUST BE AT END OF FILE) 162 | # ============================================================================ 163 | # Map focus reporting escape sequences to nothing (ZLE equivalent of .inputrc) 164 | bindkey -s '\e[I' '' 165 | bindkey -s '\e[O' '' 166 | # Ghostty's shell integration re-enables focus reporting (DECSET 1004). 167 | # This causes ^[[I and ^[[O escape sequences in claude-cli when using Cmd+Tab. 168 | # We disable it here at the END of .zshrc (after shell integration loads) 169 | # and also via precmd hook to ensure it stays disabled. 170 | 171 | # Disable immediately after shell integration loads 172 | printf '\e[?1004l' 173 | 174 | # Add precmd hook to disable before each prompt (ensures it stays disabled) 175 | _disable_focus_reporting() { 176 | printf '\e[?1004l' 177 | } 178 | 179 | # Initialize precmd_functions if it doesn't exist, then add our function 180 | [[ -z "$precmd_functions" ]] && precmd_functions=() 181 | precmd_functions+=(_disable_focus_reporting) 182 | 183 | # fnm 184 | FNM_PATH="/opt/homebrew/opt/fnm/bin" 185 | if [ -d "$FNM_PATH" ]; then 186 | eval "`fnm env`" 187 | fi 188 | 189 | # ============================================================================= 190 | # DOTFILES 191 | # ============================================================================= 192 | 193 | # Dotfiles status alias 194 | alias dfs="~/.dotfiles/scripts/sync-status.sh" 195 | 196 | # Load secrets (gitignored) 197 | [ -f ~/.secrets.env ] && source ~/.secrets.env 198 | 199 | # Warn if dotfiles sync is stale (>24 hours) 200 | ~/.dotfiles/scripts/sync-status.sh --check 2>/dev/null 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dotfiles 2 | 3 | Personal dotfiles for macOS, managed with traditional symlinks and automated sync. 4 | 5 | ## What's Included 6 | 7 | | Application | Config Location | Description | 8 | |-------------|-----------------|-------------| 9 | | **AeroSpace** | `aerospace/` | Tiling window manager for macOS | 10 | | **Ghostty** | `ghostty/` | GPU-accelerated terminal emulator | 11 | | **WezTerm** | `wezterm/` | Cross-platform terminal emulator | 12 | | **Yazi** | `yazi/` | Terminal file manager | 13 | | **Neovim** | `nvim/` | LazyVim-based Neovim configuration | 14 | | **Zsh** | `zsh/` | Shell configuration with aliases and functions | 15 | | **Homebrew** | `Brewfile` | All installed packages and casks | 16 | 17 | ## Quick Start 18 | 19 | ### On a New Machine 20 | 21 | ```bash 22 | # Clone the repo 23 | git clone https://github.com/alexfazio/dotfiles.git ~/.dotfiles 24 | 25 | # Run install script 26 | cd ~/.dotfiles && ./install.sh 27 | ``` 28 | 29 | ### What `install.sh` Does 30 | 31 | 1. Creates symlinks from `~/.config/*` to `~/.dotfiles/*` 32 | 2. Installs Homebrew packages from Brewfile 33 | 3. Installs gitleaks and pre-commit for secret protection 34 | 4. Sets up hourly auto-sync via launchd 35 | 5. Creates `~/.secrets.env` from template 36 | 37 | ## Architecture 38 | 39 | ### Symlink Structure 40 | 41 | Files live in `~/.dotfiles/` and are symlinked to their expected locations: 42 | 43 | ``` 44 | ~/.dotfiles/aerospace/aerospace.toml → ~/.config/aerospace/aerospace.toml 45 | ~/.dotfiles/ghostty/config → ~/.config/ghostty/config 46 | ~/.dotfiles/wezterm/wezterm.lua → ~/.config/wezterm/wezterm.lua 47 | ~/.dotfiles/yazi/*.toml → ~/.config/yazi/*.toml 48 | ~/.dotfiles/nvim/* → ~/.config/nvim/* 49 | ~/.dotfiles/zsh/.zshrc → ~/.zshrc 50 | ``` 51 | 52 | ### Why Symlinks? 53 | 54 | - **Industry standard**: Used by most dotfiles repos 55 | - **Git tracks actual content**: Not just symlink paths 56 | - **Simple to understand**: No magic, just file links 57 | - **Works everywhere**: No special tools required 58 | 59 | Alternative approaches considered and rejected: 60 | - **Reverse symlinks** (repo contains symlinks pointing to ~/.config): Git stores symlink paths, not file contents 61 | - **Git submodules**: Unnecessary complexity for personal configs (5-6 repos to manage) 62 | 63 | ## Secret Protection 64 | 65 | Three layers prevent accidental secret commits: 66 | 67 | ### 1. Pre-commit Hook (gitleaks) 68 | 69 | Scans staged files for API keys, tokens, and credentials before every commit. 70 | 71 | ```bash 72 | # Manual scan 73 | cd ~/.dotfiles && gitleaks detect --source . 74 | 75 | # Run all pre-commit hooks 76 | pre-commit run --all-files 77 | ``` 78 | 79 | ### 2. Comprehensive .gitignore 80 | 81 | Blocks 30+ sensitive file patterns: 82 | - `*.env`, `.env.*` (except templates) 83 | - `credentials.json`, `*.credentials` 84 | - `id_rsa`, `id_ed25519`, `*.key` 85 | - `.aws/credentials`, `*.tfvars` 86 | - And many more... 87 | 88 | ### 3. Secrets Template System 89 | 90 | API keys go in `~/.secrets.env` (gitignored), not in dotfiles: 91 | 92 | ```bash 93 | # Copy template (done by install.sh) 94 | cp ~/.dotfiles/secrets.env.template ~/.secrets.env 95 | 96 | # Edit with your actual keys 97 | vim ~/.secrets.env 98 | 99 | # Source in your shell (add to ~/.zshrc) 100 | [ -f ~/.secrets.env ] && source ~/.secrets.env 101 | ``` 102 | 103 | ## Auto-Sync System 104 | 105 | Dotfiles sync automatically every hour without manual commits. 106 | 107 | ### How It Works 108 | 109 | 1. **launchd agent** triggers `auto-sync.sh` hourly 110 | 2. **Brewfile** is regenerated to capture new packages 111 | 3. **gitleaks** scans for secrets before staging 112 | 4. **pre-commit hooks** run before commit 113 | 5. **Changes are committed and pushed** to GitHub 114 | 6. **macOS notification** alerts you if anything fails 115 | 116 | ### Monitoring 117 | 118 | ```bash 119 | # Check sync status (add alias: dfs) 120 | ~/.dotfiles/scripts/sync-status.sh 121 | 122 | # Output example: 123 | # === Dotfiles Sync Status === 124 | # Status: OK 125 | # Last sync: 2025-12-20 13:45:35 126 | # Message: Synced ff7d92c 127 | ``` 128 | 129 | ### Shell Integration 130 | 131 | The `.zshrc` is managed by this repo and includes: 132 | - `dfs` alias for checking sync status 133 | - Auto-loading of `~/.secrets.env` (gitignored) 134 | - Startup warning if sync is stale (>24 hours) 135 | 136 | ### Manual Sync 137 | 138 | ```bash 139 | # Run sync manually 140 | ~/.dotfiles/scripts/auto-sync.sh 141 | 142 | # Force sync (skip secret scan - not recommended) 143 | ~/.dotfiles/scripts/auto-sync.sh --force 144 | ``` 145 | 146 | ### Launchd Management 147 | 148 | ```bash 149 | # Check if agent is running 150 | launchctl list | grep dotfiles 151 | 152 | # Reload agent 153 | launchctl unload ~/Library/LaunchAgents/com.dotfiles.autosync.plist 154 | launchctl load ~/Library/LaunchAgents/com.dotfiles.autosync.plist 155 | 156 | # View sync logs 157 | cat /tmp/dotfiles-sync.stdout.log 158 | cat /tmp/dotfiles-sync.stderr.log 159 | ``` 160 | 161 | ## Backup & Rollback 162 | 163 | ### Automatic Backup 164 | 165 | Before first install, a timestamped backup is created: 166 | 167 | ```bash 168 | ~/.dotfiles-backup-YYYYMMDD_HHMMSS/ 169 | ├── aerospace/ 170 | ├── ghostty/ 171 | ├── wezterm/ 172 | ├── yazi/ 173 | └── nvim/ 174 | ``` 175 | 176 | ### Rollback 177 | 178 | If something goes wrong: 179 | 180 | ```bash 181 | # Restore all configs from backup 182 | ~/.dotfiles/rollback.sh 183 | 184 | # This removes symlinks and restores original files 185 | ``` 186 | 187 | ### Cleanup After Successful Setup 188 | 189 | Once everything works: 190 | 191 | ```bash 192 | BACKUP_DIR=$(cat ~/.dotfiles-backup-location) 193 | rm -rf "$BACKUP_DIR" ~/.dotfiles-backup-location 194 | ``` 195 | 196 | ## File Reference 197 | 198 | ``` 199 | ~/.dotfiles/ 200 | ├── aerospace/ 201 | │ └── aerospace.toml # Window manager config 202 | ├── ghostty/ 203 | │ ├── config # Terminal config 204 | │ └── tmux-attach.sh # Tmux integration script 205 | ├── wezterm/ 206 | │ └── wezterm.lua # Terminal config (Lua) 207 | ├── yazi/ 208 | │ ├── yazi.toml # File manager config 209 | │ ├── keymap.toml # Keybindings 210 | │ ├── package.toml # Plugin packages 211 | │ └── plugins/ # Installed plugins 212 | ├── nvim/ 213 | │ ├── init.lua # Neovim entry point 214 | │ ├── lazy-lock.json # Plugin lockfile 215 | │ ├── lazyvim.json # LazyVim config 216 | │ ├── stylua.toml # Lua formatter config 217 | │ └── lua/ # Lua config modules 218 | ├── zsh/ 219 | │ └── .zshrc # Shell config (aliases, functions, PATH) 220 | ├── scripts/ 221 | │ ├── auto-sync.sh # Automated sync with secret scanning 222 | │ ├── sync-status.sh # Health monitoring 223 | │ └── com.dotfiles.autosync.plist # launchd agent 224 | ├── Brewfile # Homebrew packages (auto-updated) 225 | ├── install.sh # New machine setup 226 | ├── rollback.sh # Restore from backup 227 | ├── secrets.env.template # API key template 228 | ├── .pre-commit-config.yaml # Pre-commit hook config 229 | ├── .gitleaks.toml # Secret scanner allowlist 230 | └── .gitignore # Sensitive file patterns 231 | ``` 232 | 233 | ## Adding New Configs 234 | 235 | To add a new application's config: 236 | 237 | 1. **Move config to dotfiles**: 238 | ```bash 239 | mv ~/.config/newapp ~/.dotfiles/newapp 240 | ``` 241 | 242 | 2. **Create symlink**: 243 | ```bash 244 | ln -sf ~/.dotfiles/newapp ~/.config/newapp 245 | ``` 246 | 247 | 3. **Update install.sh** to create the symlink on new machines 248 | 249 | 4. **Commit**: 250 | ```bash 251 | cd ~/.dotfiles && git add -A && git commit -m "Add newapp config" 252 | ``` 253 | 254 | Or just wait for auto-sync. 255 | 256 | ## Troubleshooting 257 | 258 | ### Sync Not Running 259 | 260 | ```bash 261 | # Check if launchd agent is loaded 262 | launchctl list | grep dotfiles 263 | 264 | # If not loaded, reload it 265 | launchctl load ~/Library/LaunchAgents/com.dotfiles.autosync.plist 266 | ``` 267 | 268 | ### Secret Detected (Commit Blocked) 269 | 270 | ```bash 271 | # See what was detected 272 | cd ~/.dotfiles && gitleaks detect --source . --verbose 273 | 274 | # If false positive, add to .gitleaks.toml allowlist 275 | # If real secret, remove it and add pattern to .gitignore 276 | ``` 277 | 278 | ### Symlink Broken 279 | 280 | ```bash 281 | # Re-run install script 282 | cd ~/.dotfiles && ./install.sh 283 | ``` 284 | 285 | ### Need to Restore Original Configs 286 | 287 | ```bash 288 | ~/.dotfiles/rollback.sh 289 | ``` 290 | 291 | ## License 292 | 293 | MIT 294 | -------------------------------------------------------------------------------- /aerospace/aerospace.toml: -------------------------------------------------------------------------------- 1 | # Place a copy of this config to ~/.aerospace.toml 2 | # After that, you can edit ~/.aerospace.toml to your liking 3 | 4 | # It's not necessary to copy all keys to your config. 5 | # If the key is missing in your config, "default-config.toml" will serve as a fallback 6 | 7 | # You can use it to add commands that run after AeroSpace startup. 8 | # Available commands : https://nikitabobko.github.io/AeroSpace/commands 9 | # Commands to run after AeroSpace startup 10 | after-startup-command = [] 11 | 12 | # Start AeroSpace at login 13 | start-at-login = true 14 | 15 | # Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization 16 | enable-normalization-flatten-containers = true 17 | enable-normalization-opposite-orientation-for-nested-containers = true 18 | 19 | # See: https://nikitabobko.github.io/AeroSpace/guide#layouts 20 | # The 'accordion-padding' specifies the size of accordion padding 21 | # You can set 0 to disable the padding feature 22 | accordion-padding = 30 23 | 24 | # Possible values: tiles|accordion 25 | default-root-container-layout = 'tiles' 26 | 27 | # Possible values: horizontal|vertical|auto 28 | # 'auto' means: wide monitor (anything wider than high) gets horizontal orientation, 29 | # tall monitor (anything higher than wide) gets vertical orientation 30 | default-root-container-orientation = 'auto' 31 | 32 | # Possible values: (qwerty|dvorak) 33 | # See https://nikitabobko.github.io/AeroSpace/guide#key-mapping 34 | key-mapping.preset = 'qwerty' 35 | 36 | # Mouse follows focus when focused monitor changes 37 | # Drop it from your config, if you don't like this behavior 38 | # See https://nikitabobko.github.io/AeroSpace/guide#on-focus-changed-callbacks 39 | # See https://nikitabobko.github.io/AeroSpace/commands#move-mouse 40 | # on-focused-monitor-changed = ['move-mouse monitor-lazy-center'] 41 | # on-focus-changed = "move-mouse window-lazy-center" 42 | 43 | # Gaps between windows (inner-*) and between monitor edges (outer-*). 44 | # Possible values: 45 | # - Constant: gaps.outer.top = 8 46 | # - Per monitor: gaps.outer.top = [{ monitor.main = 16 }, { monitor."some-pattern" = 32 }, 24] 47 | # In this example, 24 is a default value when there is no match. 48 | # Monitor pattern is the same as for 'workspace-to-monitor-force-assignment'. 49 | # See: https://nikitabobko.github.io/AeroSpace/guide#assign-workspaces-to-monitors 50 | [gaps] 51 | inner.horizontal = 18 52 | inner.vertical = 18 53 | outer.left = 18 54 | outer.bottom = 18 55 | outer.top = 18 56 | outer.right = 18 57 | 58 | # 'main' binding mode declaration 59 | # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes 60 | # 'main' binding mode must be always presented 61 | [mode.main.binding] 62 | 63 | # All possible keys: 64 | # - Letters. a, b, c, ..., z 65 | # - Numbers. 0, 1, 2, ..., 9 66 | # - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9 67 | # - F-keys. f1, f2, ..., f20 68 | # - Special keys. minus, equal, period, comma, slash, backslash, quote, semicolon, backtick, 69 | # leftSquareBracket, rightSquareBracket, space, enter, esc, backspace, tab 70 | # - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual, 71 | # keypadMinus, keypadMultiply, keypadPlus 72 | # - Arrows. left, down, up, right 73 | 74 | # All possible modifiers: cmd, alt, ctrl, shift 75 | 76 | # All possible commands: https://nikitabobko.github.io/AeroSpace/commands 77 | 78 | # You can uncomment this line to open up terminal with alt + enter shortcut 79 | # See: https://nikitabobko.github.io/AeroSpace/commands#exec-and-forget 80 | # alt-enter = 'exec-and-forget open -n /System/Applications/Utilities/Terminal.app' 81 | 82 | # See: https://nikitabobko.github.io/AeroSpace/commands#layout 83 | cmd-ctrl-alt-shift-slash = 'layout tiles horizontal vertical' 84 | cmd-ctrl-alt-shift-comma = 'layout accordion horizontal vertical' 85 | 86 | # See: https://nikitabobko.github.io/AeroSpace/commands#focus 87 | alt-h = 'focus left' 88 | alt-j = 'focus down' 89 | alt-k = 'focus up' 90 | alt-l = 'focus right' 91 | 92 | # See: https://nikitabobko.github.io/AeroSpace/commands#move 93 | cmd-ctrl-alt-shift-h = 'move left' 94 | cmd-ctrl-alt-shift-j = 'move down' 95 | cmd-ctrl-alt-shift-k = 'move up' 96 | cmd-ctrl-alt-shift-l = 'move right' 97 | 98 | 99 | # See: https://nikitabobko.github.io/AeroSpace/commands#workspace 100 | alt-m = 'workspace MSG' 101 | alt-n = 'workspace NTS' 102 | alt-p = 'workspace PRG' 103 | alt-r = 'workspace REM' 104 | alt-t = 'workspace TER' 105 | alt-v = 'workspace VID' 106 | alt-w = 'workspace WEB' 107 | alt-a = 'workspace AI' 108 | alt-d = 'workspace DIR' 109 | alt-g = 'workspace GFX' 110 | 111 | # See: https://nikitabobko.github.io/AeroSpace/commands#move-node-to-workspace 112 | cmd-ctrl-alt-shift-m = 'move-node-to-workspace MSG' 113 | cmd-ctrl-alt-shift-n = 'move-node-to-workspace NTS' 114 | cmd-ctrl-alt-shift-p = 'move-node-to-workspace PRG' 115 | cmd-ctrl-alt-shift-r = 'move-node-to-workspace REM' 116 | cmd-ctrl-alt-shift-t = 'move-node-to-workspace TER' 117 | cmd-ctrl-alt-shift-v = 'move-node-to-workspace VID' 118 | cmd-ctrl-alt-shift-w = 'move-node-to-workspace WEB' 119 | cmd-ctrl-alt-shift-a = 'move-node-to-workspace AI' 120 | cmd-ctrl-alt-shift-d = 'move-node-to-workspace DIR' 121 | cmd-ctrl-alt-shift-g = 'move-node-to-workspace GFX' 122 | 123 | # Note: D workspace move command uses 'd' for directory, 'f' available for fullscreen 124 | cmd-ctrl-alt-shift-f = 'fullscreen --no-outer-gaps' 125 | cmd-ctrl-alt-shift-equal = 'fullscreen --no-outer-gaps' 126 | 127 | # See: https://nikitabobko.github.io/AeroSpace/commands#workspace-back-and-forth 128 | alt-tab = 'workspace-back-and-forth' 129 | # See: https://nikitabobko.github.io/AeroSpace/commands#move-workspace-to-monitor 130 | cmd-ctrl-alt-shift-tab = 'move-workspace-to-monitor --wrap-around next' 131 | 132 | # See: https://nikitabobko.github.io/AeroSpace/commands#mode 133 | cmd-ctrl-alt-shift-semicolon = 'mode service' 134 | 135 | # Note: This conflicts with the R workspace move command above 136 | # You may want to change this to a different key combination 137 | cmd-ctrl-alt-shift-z = 'mode resize' 138 | 139 | [mode.resize.binding] 140 | h = 'resize width -50' 141 | j = 'resize height +50' 142 | k = 'resize height -50' 143 | l = 'resize width +50' 144 | b = 'balance-sizes' 145 | 146 | 147 | # See: https://nikitabobko.github.io/AeroSpace/commands#resize 148 | minus = 'resize smart -50' 149 | equal = 'resize smart +50' 150 | 151 | enter = 'mode main' 152 | esc = 'mode main' 153 | 154 | # 'service' binding mode declaration. 155 | # See: https://nikitabobko.github.io/AeroSpace/guide#binding-modes 156 | [mode.service.binding] 157 | esc = ['reload-config', 'mode main'] 158 | r = ['flatten-workspace-tree', 'mode main'] # reset layout 159 | #s = ['layout sticky tiling', 'mode main'] # sticky is not yet supported https://github.com/nikitabobko/AeroSpace/issues/2 160 | f = [ 161 | 'layout floating tiling', 162 | 'mode main', 163 | ] # Toggle between floating and tiling layout 164 | backspace = ['close-all-windows-but-current', 'mode main'] 165 | 166 | ctrl-shift-h = ['join-with left', 'mode main'] 167 | ctrl-shift-j = ['join-with down', 'mode main'] 168 | ctrl-shift-k = ['join-with up', 'mode main'] 169 | ctrl-shift-l = ['join-with right', 'mode main'] 170 | 171 | # Application window detection rules 172 | # Note: Use `osascript -e 'id of app "AppName"'` to detect correct app bundle identifiers 173 | 174 | # Ghostty - float to prevent tabs from being managed as separate windows 175 | # IMPORTANT: Use Ghostty's native tab switching, not AeroSpace focus: 176 | # - Cmd+Shift+] = next tab 177 | # - Cmd+Shift+[ = previous tab 178 | # - Cmd+1/2/3... = jump to tab 179 | # See: https://ghostty.org/docs/help/macos-tiling-wms 180 | [[on-window-detected]] 181 | if.app-id = 'com.mitchellh.ghostty' 182 | run = ['layout floating', 'move-node-to-workspace TER'] 183 | 184 | [[on-window-detected]] 185 | if.app-id = 'com.TickTick.task.mac' 186 | run = "layout floating" 187 | 188 | # Password manager - always float 189 | [[on-window-detected]] 190 | if.app-id = 'com.bitwarden.desktop' 191 | run = "layout floating" 192 | 193 | [[on-window-detected]] 194 | if.app-id = 'com.github.wez.wezterm' 195 | run = "move-node-to-workspace TER" 196 | 197 | [[on-window-detected]] 198 | if.app-id = 'com.apple.Terminal' 199 | run = "move-node-to-workspace PRG" 200 | 201 | [[on-window-detected]] 202 | if.app-id = 'com.goodsnooze.MacWhisper' 203 | run = "move-node-to-workspace VID" 204 | 205 | # Browser extension popups for Chrome - must come BEFORE general Chrome rule 206 | [[on-window-detected]] 207 | if.app-id = 'com.google.Chrome' 208 | if.window-title-regex-substring = '(extension|popup|1password|bitwarden|lastpass|dashlane)' 209 | run = 'layout floating' 210 | 211 | [[on-window-detected]] 212 | if.app-id = 'com.google.Chrome' 213 | run = "move-node-to-workspace WEB" 214 | 215 | # incide-portable (Tauri dev builds have NULL bundle ID, so match by app name) 216 | [[on-window-detected]] 217 | if.app-name-regex-substring = 'incide-portable' 218 | run = "move-node-to-workspace WEB" 219 | 220 | [[on-window-detected]] 221 | if.app-id = 'io.remnote' 222 | run = "move-node-to-workspace REM" 223 | 224 | # Browser extension popups for Arc - must come BEFORE general Arc rule 225 | [[on-window-detected]] 226 | if.app-id = 'company.thebrowser.Browser' 227 | if.window-title-regex-substring = '(extension|popup|1password|bitwarden|lastpass|dashlane)' 228 | run = 'layout floating' 229 | 230 | # Browser apps 231 | [[on-window-detected]] 232 | if.app-id = 'app.zen-browser.zen' 233 | run = "move-node-to-workspace WEB" 234 | 235 | [[on-window-detected]] 236 | if.app-id = 'org.mozilla.firefox' 237 | run = "move-node-to-workspace WEB" 238 | 239 | [[on-window-detected]] 240 | if.app-id = 'company.thebrowser.Browser' 241 | run = "move-node-to-workspace WEB" 242 | 243 | # Messaging apps 244 | [[on-window-detected]] 245 | if.app-id = 'com.tdesktop.Telegram' 246 | run = "move-node-to-workspace MSG" 247 | 248 | [[on-window-detected]] 249 | if.app-id = 'net.whatsapp.WhatsApp' 250 | run = "move-node-to-workspace MSG" 251 | 252 | [[on-window-detected]] 253 | if.app-id = 'com.freron.MailMate' 254 | run = "move-node-to-workspace MSG" 255 | 256 | [[on-window-detected]] 257 | if.app-id = 'com.automattic.beeper.desktop' 258 | run = "move-node-to-workspace MSG" 259 | 260 | [[on-window-detected]] 261 | if.app-id = 'com.tinyspeck.slackmacgap' 262 | run = "move-node-to-workspace MSG" 263 | 264 | # Code editors 265 | [[on-window-detected]] 266 | if.app-id = 'dev.zed.Zed' 267 | run = "move-node-to-workspace PRG" 268 | 269 | [[on-window-detected]] 270 | if.app-id = 'com.todesktop.230313mzl4w4u92' 271 | run = "move-node-to-workspace PRG" 272 | 273 | # AI Chatbots 274 | [[on-window-detected]] 275 | if.app-id = 'com.openai.chat' 276 | run = "move-node-to-workspace AI" 277 | 278 | [[on-window-detected]] 279 | if.app-id = 'com.anthropic.claudefordesktop' 280 | run = "move-node-to-workspace AI" 281 | 282 | [[on-window-detected]] 283 | if.app-id = 'co.podzim.BoltGPT-setapp' 284 | run = "move-node-to-workspace AI" 285 | 286 | # File manager - Move to DIR workspace and make floating 287 | [[on-window-detected]] 288 | if.app-id = 'com.apple.finder' 289 | run = ['layout floating', 'move-node-to-workspace DIR'] 290 | 291 | # Video editing apps 292 | [[on-window-detected]] 293 | if.app-id = 'com.lemon.lvoverseas' 294 | run = "move-node-to-workspace VID" 295 | 296 | [[on-window-detected]] 297 | if.app-id = 'com.charliemonroe.Downie-setapp' 298 | run = "move-node-to-workspace VID" 299 | 300 | [[on-window-detected]] 301 | if.app-id = 'com.TechSmith.Snagit2024' 302 | run = "move-node-to-workspace VID" 303 | 304 | [[on-window-detected]] 305 | if.app-id = 'com.agiletortoise.Drafts-OSX' 306 | run = "move-node-to-workspace NTS" 307 | 308 | [[on-window-detected]] 309 | if.app-id = 'notion.id' 310 | run = "move-node-to-workspace NTS" 311 | 312 | [[on-window-detected]] 313 | if.app-id = 'com.apple.MobileSMS' 314 | run = "move-node-to-workspace MSG" 315 | 316 | # Graphics/Photo editing apps 317 | [[on-window-detected]] 318 | if.app-id = 'com.pixelmatorteam.pixelmator.x' 319 | run = ['layout floating', 'move-node-to-workspace GFX'] 320 | 321 | # Screen Studio - always float 322 | [[on-window-detected]] 323 | if.app-id = 'com.timpler.screenstudio' 324 | run = 'layout floating' 325 | 326 | # Force float system settings (updated for modern macOS) 327 | [[on-window-detected]] 328 | if.app-id = 'com.apple.systemsettings' 329 | run = 'layout floating' 330 | 331 | # Force float legacy system preferences (for older macOS) 332 | [[on-window-detected]] 333 | if.app-id = 'com.apple.systempreferences' 334 | run = 'layout floating' 335 | 336 | # Enhanced dialog pattern matching 337 | [[on-window-detected]] 338 | if.window-title-regex-substring = '(dialog|alert|preferences|settings|about|error|warning|confirm|properties|options|configuration)' 339 | check-further-callbacks = true 340 | run = 'layout floating' 341 | 342 | # Enhanced file dialog matching 343 | [[on-window-detected]] 344 | if.window-title-regex-substring = '(open|save|choose|select|import|export|browse|download|upload)' 345 | check-further-callbacks = true 346 | run = 'layout floating' 347 | 348 | # # Common application dialogs by app bundle 349 | # [[on-window-detected]] 350 | # if.app-id = 'com.apple.finder' 351 | # if.window-title-regex-substring = '(copy|move|delete|trash|info|get info)' 352 | # run = 'layout floating' 353 | 354 | # Adobe application dialogs 355 | [[on-window-detected]] 356 | if.app-id = 'com.adobe.Photoshop' 357 | if.window-title-regex-substring = '(new|open|save|export|import|color|brush|layer)' 358 | run = 'layout floating' 359 | -------------------------------------------------------------------------------- /wezterm/wezterm.lua: -------------------------------------------------------------------------------- 1 | local wezterm = require 'wezterm' 2 | local act = wezterm.action 3 | local mux = wezterm.mux 4 | 5 | local config = {} 6 | 7 | if wezterm.config_builder then 8 | config = wezterm.config_builder() 9 | end 10 | 11 | -- ======================================== 12 | -- Session Management: Using WezTerm's Built-in Workspaces 13 | -- ======================================== 14 | -- Note: Resurrect plugin has bugs, using native workspace management instead 15 | -- Workspaces persist automatically between sessions 16 | 17 | -- 4-way split on startup (only on fresh mux start, not when reconnecting) 18 | -- Check if mux already has windows to determine if this is a reconnection 19 | wezterm.on('gui-startup', function(cmd) 20 | -- If mux already has windows, we're reconnecting to an existing session 21 | -- Skip creating new layout to preserve existing panes 22 | local windows = mux.all_windows() 23 | if #windows > 0 then 24 | return 25 | end 26 | 27 | -- Fresh mux start - create 4-way split layout 28 | local tab, pane, window = mux.spawn_window(cmd or {}) 29 | 30 | -- Split horizontally (top/bottom) 31 | local bottom_pane = pane:split { 32 | direction = 'Bottom', 33 | size = 0.5, 34 | } 35 | 36 | -- Split top pane vertically (left/right) 37 | pane:split { 38 | direction = 'Right', 39 | size = 0.5, 40 | } 41 | 42 | -- Split bottom pane vertically (left/right) 43 | bottom_pane:split { 44 | direction = 'Right', 45 | size = 0.5, 46 | } 47 | end) 48 | 49 | -- Terminal configuration 50 | -- Note: Removing term override to use WezTerm's optimized default terminal type 51 | -- config.term = 'xterm-256color' 52 | 53 | -- Key handling 54 | config.send_composed_key_when_left_alt_is_pressed = false 55 | config.send_composed_key_when_right_alt_is_pressed = false 56 | -- Enable CSI u encoding for better key handling support 57 | config.enable_csi_u_key_encoding = true 58 | 59 | -- Font configuration with fallbacks (JetBrains Mono Nerd Font - same as Ghostty default + icons) 60 | config.font = wezterm.font_with_fallback({ 61 | 'JetBrainsMono NF', 62 | 'JetBrains Mono', 63 | 'Menlo', 64 | 'Noto Color Emoji', 65 | }) 66 | config.font_size = 18.0 67 | 68 | -- Enable scrollback 69 | config.scrollback_lines = 10000 -- Increase scrollback history 70 | 71 | -- Scroll bar configuration 72 | config.enable_scroll_bar = true 73 | config.min_scroll_bar_height = "0.5cell" -- Minimum height of scroll bar thumb (default) 74 | 75 | -- Color scheme: Auto-switch between Catppuccin Frappe (dark) and Latte (light) 76 | -- based on system appearance 77 | local function scheme_for_appearance(appearance) 78 | if appearance:find('Dark') then 79 | return 'Catppuccin Frappe' 80 | else 81 | return 'Catppuccin Latte' 82 | end 83 | end 84 | 85 | -- Safely get appearance (wezterm.gui is not available to mux server) 86 | local function get_appearance() 87 | if wezterm.gui then 88 | return wezterm.gui.get_appearance() 89 | end 90 | return 'Dark' -- Default to dark when running as mux server 91 | end 92 | 93 | -- Set initial color scheme based on current system appearance 94 | config.color_scheme = scheme_for_appearance(get_appearance()) 95 | 96 | -- Automatically switch theme when system appearance changes 97 | wezterm.on('window-config-reloaded', function(window, pane) 98 | local overrides = window:get_config_overrides() or {} 99 | local appearance = window:get_appearance() 100 | local scheme = scheme_for_appearance(appearance) 101 | if overrides.color_scheme ~= scheme then 102 | overrides.color_scheme = scheme 103 | window:set_config_overrides(overrides) 104 | end 105 | end) 106 | 107 | -- Tab bar appearance and customization 108 | config.hide_tab_bar_if_only_one_tab = true 109 | config.tab_bar_at_bottom = false 110 | config.tab_max_width = 32 -- Prevent excessively long tab titles 111 | config.use_fancy_tab_bar = true -- Enable the fancy tab bar with better styling 112 | config.window_frame = { 113 | font_size = 14.0, -- Larger tab bar font (default is 12pt on macOS) 114 | } 115 | 116 | -- Window appearance 117 | config.window_background_opacity = 1.0 118 | 119 | -- Enable support for unicode characters 120 | config.warn_about_missing_glyphs = false 121 | 122 | -- Scrolling and performance optimization 123 | config.alternate_buffer_wheel_scroll_speed = 5 -- Smooth scrolling in alternate buffers 124 | config.animation_fps = 120 -- Smooth animations 125 | config.max_fps = 120 -- Higher FPS for TUI responsiveness 126 | 127 | -- Default shell and startup behavior 128 | config.default_prog = { '/bin/zsh' } -- Use zsh as default shell 129 | config.initial_cols = 160 -- Initial window width 130 | config.initial_rows = 40 -- Initial window height 131 | 132 | -- ======================================== 133 | -- Pane Persistence: Keep panes open after process exits 134 | -- ======================================== 135 | -- "Hold" keeps panes open for inspection after shell/process exits 136 | -- Alternatives: "Close" (immediate), "CloseOnCleanExit" (only on success) 137 | config.exit_behavior = "Hold" 138 | 139 | -- ======================================== 140 | -- Session Persistence: Multiplexer Daemon 141 | -- ======================================== 142 | -- Enable WezTerm's built-in multiplexer for true session persistence 143 | -- This keeps your panes, tabs, and workspaces alive even when you close WezTerm 144 | config.unix_domains = { 145 | { 146 | name = 'unix', 147 | }, 148 | } 149 | 150 | -- Automatically connect to the multiplexer daemon on startup 151 | -- If the daemon isn't running, WezTerm will start it automatically 152 | config.default_gui_startup_args = { 'connect', 'unix' } 153 | 154 | -- Bell sound settings 155 | config.audible_bell = "Disabled" 156 | 157 | -- Copy on select 158 | config.selection_word_boundary = " \t\n{}[]()\"'`,;:" 159 | 160 | -- Mouse and interaction settings 161 | config.mouse_bindings = { 162 | { 163 | event = { Down = { streak = 1, button = 'Right' } }, 164 | mods = 'NONE', 165 | action = act.PasteFrom('Clipboard'), -- Right-click to paste 166 | }, 167 | } 168 | 169 | -- Hyperlink handling for better URL support 170 | config.check_for_updates = false -- Disable auto-update checks 171 | 172 | -- Vi-style copy mode configuration 173 | config.key_tables = { 174 | copy_mode = { 175 | {key = 'Tab', mods = 'NONE', action = act.CopyMode('MoveForwardWord')}, 176 | {key = 'Tab', mods = 'SHIFT', action = act.CopyMode('MoveBackwardWord')}, 177 | {key = 'Enter', mods = 'NONE', action = act.CopyMode('MoveToStartOfNextLine')}, 178 | {key = 'Escape', mods = 'NONE', action = act.CopyMode('Close')}, 179 | {key = 'Space', mods = 'NONE', action = act.CopyMode({SetSelectionMode = 'Cell'})}, 180 | {key = '$', mods = 'NONE', action = act.CopyMode('MoveToEndOfLineContent')}, 181 | {key = '$', mods = 'SHIFT', action = act.CopyMode('MoveToEndOfLineContent')}, 182 | {key = ',', mods = 'NONE', action = act.CopyMode('JumpReverse')}, 183 | {key = '0', mods = 'NONE', action = act.CopyMode('MoveToStartOfLine')}, 184 | {key = ';', mods = 'NONE', action = act.CopyMode('JumpAgain')}, 185 | {key = 'F', mods = 'NONE', action = act.CopyMode({JumpBackward = {prev_char = false}})}, 186 | {key = 'F', mods = 'SHIFT', action = act.CopyMode({JumpBackward = {prev_char = false}})}, 187 | {key = 'G', mods = 'NONE', action = act.CopyMode('MoveToScrollbackBottom')}, 188 | {key = 'G', mods = 'SHIFT', action = act.CopyMode('MoveToScrollbackBottom')}, 189 | {key = 'H', mods = 'NONE', action = act.CopyMode('MoveToViewportTop')}, 190 | {key = 'H', mods = 'SHIFT', action = act.CopyMode('MoveToViewportTop')}, 191 | {key = 'L', mods = 'NONE', action = act.CopyMode('MoveToViewportBottom')}, 192 | {key = 'L', mods = 'SHIFT', action = act.CopyMode('MoveToViewportBottom')}, 193 | {key = 'M', mods = 'NONE', action = act.CopyMode('MoveToViewportMiddle')}, 194 | {key = 'M', mods = 'SHIFT', action = act.CopyMode('MoveToViewportMiddle')}, 195 | {key = 'O', mods = 'NONE', action = act.CopyMode('MoveToSelectionOtherEndHoriz')}, 196 | {key = 'O', mods = 'SHIFT', action = act.CopyMode('MoveToSelectionOtherEndHoriz')}, 197 | {key = 'T', mods = 'NONE', action = act.CopyMode({JumpBackward = {prev_char = true}})}, 198 | {key = 'T', mods = 'SHIFT', action = act.CopyMode({JumpBackward = {prev_char = true}})}, 199 | {key = 'V', mods = 'NONE', action = act.CopyMode({SetSelectionMode = 'Line'})}, 200 | {key = 'V', mods = 'SHIFT', action = act.CopyMode({SetSelectionMode = 'Line'})}, 201 | {key = '^', mods = 'NONE', action = act.CopyMode('MoveToStartOfLineContent')}, 202 | {key = '^', mods = 'SHIFT', action = act.CopyMode('MoveToStartOfLineContent')}, 203 | {key = 'b', mods = 'NONE', action = act.CopyMode('MoveBackwardWord')}, 204 | {key = 'b', mods = 'ALT', action = act.CopyMode('MoveBackwardWord')}, 205 | {key = 'b', mods = 'CTRL', action = act.CopyMode('PageUp')}, 206 | {key = 'c', mods = 'CTRL', action = act.CopyMode('Close')}, 207 | {key = 'd', mods = 'CTRL', action = act.CopyMode({MoveByPage = (0.5)})}, 208 | {key = 'e', mods = 'NONE', action = act.CopyMode('MoveForwardWordEnd')}, 209 | {key = 'f', mods = 'NONE', action = act.CopyMode({JumpForward = {prev_char = false}})}, 210 | {key = 'f', mods = 'ALT', action = act.CopyMode('MoveForwardWord')}, 211 | {key = 'f', mods = 'CTRL', action = act.CopyMode('PageDown')}, 212 | {key = 'g', mods = 'NONE', action = act.CopyMode('MoveToScrollbackTop')}, 213 | {key = 'g', mods = 'CTRL', action = act.CopyMode('Close')}, 214 | {key = 'h', mods = 'NONE', action = act.CopyMode('MoveLeft')}, 215 | {key = 'j', mods = 'NONE', action = act.CopyMode('MoveDown')}, 216 | {key = 'k', mods = 'NONE', action = act.CopyMode('MoveUp')}, 217 | {key = 'l', mods = 'NONE', action = act.CopyMode('MoveRight')}, 218 | {key = 'm', mods = 'ALT', action = act.CopyMode('MoveToStartOfLineContent')}, 219 | {key = 'o', mods = 'NONE', action = act.CopyMode('MoveToSelectionOtherEnd')}, 220 | {key = 'q', mods = 'NONE', action = act.CopyMode('Close')}, 221 | {key = 't', mods = 'NONE', action = act.CopyMode({JumpForward = {prev_char = true}})}, 222 | {key = 'u', mods = 'CTRL', action = act.CopyMode({MoveByPage = (-0.5)})}, 223 | {key = 'v', mods = 'NONE', action = act.CopyMode({SetSelectionMode = 'Cell'})}, 224 | {key = 'v', mods = 'CTRL', action = act.CopyMode({SetSelectionMode = 'Block'})}, 225 | {key = 'w', mods = 'NONE', action = act.CopyMode('MoveForwardWord')}, 226 | {key = 'y', mods = 'NONE', action = act.Multiple({{CopyTo = 'ClipboardAndPrimarySelection'}, {CopyMode = 'Close'}})}, 227 | {key = 'PageUp', mods = 'NONE', action = act.CopyMode('PageUp')}, 228 | {key = 'PageDown', mods = 'NONE', action = act.CopyMode('PageDown')}, 229 | {key = 'End', mods = 'NONE', action = act.CopyMode('MoveToEndOfLineContent')}, 230 | {key = 'Home', mods = 'NONE', action = act.CopyMode('MoveToStartOfLine')}, 231 | {key = 'LeftArrow', mods = 'NONE', action = act.CopyMode('MoveLeft')}, 232 | {key = 'LeftArrow', mods = 'ALT', action = act.CopyMode('MoveBackwardWord')}, 233 | {key = 'RightArrow', mods = 'NONE', action = act.CopyMode('MoveRight')}, 234 | {key = 'RightArrow', mods = 'ALT', action = act.CopyMode('MoveForwardWord')}, 235 | {key = 'UpArrow', mods = 'NONE', action = act.CopyMode('MoveUp')}, 236 | {key = 'DownArrow', mods = 'NONE', action = act.CopyMode('MoveDown')}, 237 | } 238 | } 239 | 240 | -- Leader key and keybindings configuration 241 | -- Leader: CMD+Space (macOS-native, avoids Neovim conflicts, timeout after 1000ms) 242 | config.leader = {key = 'Space', mods = 'CMD', timeout_milliseconds = 1000} 243 | 244 | config.keys = { 245 | -- ======================================== 246 | -- Copy Mode 247 | -- ======================================== 248 | {mods = 'LEADER', key = '[', action = act.ActivateCopyMode}, 249 | 250 | -- ======================================== 251 | -- Pane Navigation (vim-style hjkl) 252 | -- ======================================== 253 | -- Direct navigation with CMD+hjkl (sends ESC first to exit insert mode) 254 | {key = 'h', mods = 'CMD', action = act.Multiple { act.SendKey { key = 'Escape' }, act.ActivatePaneDirection('Left') }}, 255 | {key = 'j', mods = 'CMD', action = act.Multiple { act.SendKey { key = 'Escape' }, act.ActivatePaneDirection('Down') }}, 256 | {key = 'k', mods = 'CMD', action = act.Multiple { act.SendKey { key = 'Escape' }, act.ActivatePaneDirection('Up') }}, 257 | {key = 'l', mods = 'CMD', action = act.Multiple { act.SendKey { key = 'Escape' }, act.ActivatePaneDirection('Right') }}, 258 | 259 | -- ======================================== 260 | -- Pane Resizing (Shift + hjkl) 261 | -- ======================================== 262 | {mods = 'LEADER|SHIFT', key = 'H', action = act.AdjustPaneSize({'Left', 5})}, 263 | {mods = 'LEADER|SHIFT', key = 'J', action = act.AdjustPaneSize({'Down', 5})}, 264 | {mods = 'LEADER|SHIFT', key = 'K', action = act.AdjustPaneSize({'Up', 5})}, 265 | {mods = 'LEADER|SHIFT', key = 'L', action = act.AdjustPaneSize({'Right', 5})}, 266 | 267 | -- ======================================== 268 | -- Pane Splitting 269 | -- ======================================== 270 | -- Tmux-style splits 271 | {mods = 'LEADER', key = '-', action = act.SplitPane {direction = 'Down', size = {Percent = 50}}}, 272 | {mods = 'LEADER', key = '|', action = act.SplitPane {direction = 'Right', size = {Percent = 50}}}, 273 | 274 | -- Vim-style splits 275 | {mods = 'LEADER', key = 'v', action = act.SplitPane {direction = 'Right', size = {Percent = 50}}}, -- Like :vsplit 276 | {mods = 'LEADER', key = 's', action = act.SplitPane {direction = 'Down', size = {Percent = 50}}}, -- Like :split 277 | 278 | -- ======================================== 279 | -- Pane Management 280 | -- ======================================== 281 | {mods = 'LEADER', key = 'x', action = act.CloseCurrentPane({confirm = true})}, 282 | {mods = 'LEADER', key = 'q', action = act.CloseCurrentPane({confirm = true})}, -- Like :q in vim 283 | {key = 'w', mods = 'CMD', action = act.CloseCurrentPane({confirm = true})}, -- Close pane (override default tab close) 284 | {key = 'w', mods = 'CMD|SHIFT', action = act.CloseCurrentTab({confirm = true})}, -- Close entire tab 285 | {mods = 'LEADER', key = 'z', action = act.TogglePaneZoomState}, 286 | 287 | -- ======================================== 288 | -- Workspace Management (Built-in WezTerm Features) 289 | -- ======================================== 290 | -- Show workspace switcher (fuzzy find all workspaces) 291 | {mods = 'ALT', key = 'w', action = act.ShowLauncherArgs({flags = 'FUZZY|WORKSPACES'})}, 292 | 293 | -- Create/switch to named workspace 294 | {mods = 'ALT|SHIFT', key = 'N', action = act.PromptInputLine({ 295 | description = 'Enter new workspace name:', 296 | action = wezterm.action_callback(function(window, pane, line) 297 | if line then 298 | window:perform_action(act.SwitchToWorkspace({name = line}), pane) 299 | end 300 | end), 301 | })}, 302 | 303 | -- Rename current workspace 304 | {mods = 'ALT|SHIFT', key = 'R', action = act.PromptInputLine({ 305 | description = 'Rename workspace to:', 306 | action = wezterm.action_callback(function(window, pane, line) 307 | if line then 308 | mux.rename_workspace(mux.get_active_workspace(), line) 309 | end 310 | end), 311 | })}, 312 | } 313 | 314 | return config 315 | -------------------------------------------------------------------------------- /yazi/plugins/clipboard.yazi/LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | --------------------------------------------------------------------------------