├── 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 |
--------------------------------------------------------------------------------