dotedit 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #!/usr/bin/env python3
  2. """Quick editor for dotfiles directories"""
  3. import os
  4. import subprocess
  5. import sys
  6. from pathlib import Path
  7. DOTFILES_DIR = Path.home() / "dotfiles"
  8. def find_config_dir(base_dir):
  9. """Find the actual config directory within a dotfiles package."""
  10. base_path = Path(base_dir)
  11. # Common patterns: .config/package, .config/package/package, etc.
  12. common_paths = [
  13. base_path / ".config" / base_path.name, # ~/dotfiles/niri/.config/niri
  14. base_path / ".config", # ~/dotfiles/niri/.config
  15. base_path, # fallback to base
  16. ]
  17. # Check for .config/package pattern first
  18. config_package = base_path / ".config" / base_path.name
  19. if config_package.exists() and config_package.is_dir():
  20. return config_package
  21. # Check for .config directory
  22. config_dir = base_path / ".config"
  23. if config_dir.exists() and config_dir.is_dir():
  24. return config_dir
  25. # Fallback to base directory
  26. return base_path
  27. def get_dotfiles_dirs():
  28. """Get list of dotfiles directories with their config paths."""
  29. if not DOTFILES_DIR.exists():
  30. return []
  31. dirs = []
  32. for item in sorted(DOTFILES_DIR.iterdir()):
  33. if item.is_dir() and not item.name.startswith("."):
  34. config_path = find_config_dir(item)
  35. dirs.append((item.name, config_path))
  36. return dirs
  37. def main():
  38. dirs_configs = get_dotfiles_dirs()
  39. if not dirs_configs:
  40. sys.exit(1)
  41. # Format with icons/emojis for better visual appeal
  42. formatted_dirs = []
  43. name_to_config = {}
  44. icons = {
  45. "niri": "🪟",
  46. "zsh": "🐚",
  47. "alacritty": "💻",
  48. "kitty": "🐱",
  49. "ghostty": "👻",
  50. "git": "📦",
  51. "fuzzel": "🔍",
  52. "nvim": "⚡",
  53. "vim": "⚡",
  54. "tmux": "📺",
  55. "zellij": "📺",
  56. "env": "🌍",
  57. "fonts": "🔤",
  58. "applications": "📱",
  59. }
  60. for dir_name, config_path in dirs_configs:
  61. icon = icons.get(dir_name.lower(), "📁")
  62. formatted_dirs.append(f"{icon} {dir_name}")
  63. name_to_config[dir_name] = config_path
  64. # Present in fuzzel with styling
  65. fuzzel_process = subprocess.Popen(
  66. [
  67. "fuzzel",
  68. "--dmenu",
  69. "--prompt", "📝 Edit: ",
  70. "--width", "40",
  71. "--lines", "15",
  72. "--border-width", "2",
  73. "--background-color", "#191724ff",
  74. "--text-color", "#e0def4ff",
  75. "--match-color", "#31748fff",
  76. "--selection-color", "#1f1d2eff",
  77. "--selection-text-color", "#31748fff",
  78. "--selection-match-color", "#31748fff",
  79. "--prompt-color", "#f6c177ff",
  80. ],
  81. stdin=subprocess.PIPE,
  82. stdout=subprocess.PIPE,
  83. text=True,
  84. )
  85. stdout, _ = fuzzel_process.communicate(input="\n".join(formatted_dirs))
  86. selected = stdout.strip()
  87. if not selected:
  88. sys.exit(0)
  89. # Remove icon and extract directory name
  90. selected_dir = selected.split(" ", 1)[-1] if " " in selected else selected
  91. selected_path = name_to_config.get(selected_dir)
  92. if not selected_path or not selected_path.is_dir():
  93. sys.exit(1)
  94. # Open alacritty with nvim in the actual config directory
  95. subprocess.Popen(
  96. ["alacritty", f"--working-directory={selected_path}", "-e", "nvim"],
  97. start_new_session=True,
  98. )
  99. if __name__ == "__main__":
  100. main()