a simple git-manager ui
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

a simple git ui

Kumar vaibhav fed74152

+171
+1
.gitignore
··· 1 + /target
__pycache__/gitui.cpython-314.pyc

This is a binary file and will not be displayed.

+168
gitui.py
··· 1 + #!/usr/bin/env python3 2 + import curses 3 + import subprocess 4 + import os 5 + 6 + class GitUI: 7 + def __init__(self, stdscr): 8 + self.s = stdscr 9 + self.s.keypad(True) 10 + curses.start_color() 11 + curses.use_default_colors() 12 + # Catppuccin Mocha palette using curses color pairs 13 + curses.init_pair(1, curses.COLOR_CYAN, curses.COLOR_BLACK) 14 + curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) 15 + curses.init_pair(3, curses.COLOR_GREEN, curses.COLOR_BLACK) 16 + curses.init_pair(4, curses.COLOR_RED, curses.COLOR_BLACK) 17 + curses.init_pair(5, curses.COLOR_YELLOW, curses.COLOR_BLACK) 18 + curses.init_pair(6, curses.COLOR_MAGENTA, curses.COLOR_BLACK) 19 + curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_BLACK) 20 + 21 + self.menu = ["Commit", "Push", "Remote Add", "Stash", "New Branch", "Quit"] 22 + self.sel = 0 23 + self.msg = "" 24 + self.input = "" 25 + self.input_mode = False 26 + self.prompt = "" 27 + self.cb = None 28 + self.running = True 29 + 30 + def git(self, *args): 31 + try: 32 + r = subprocess.run(["git"] + list(args), capture_output=True, text=True, timeout=10) 33 + return r.stdout.strip() or r.stderr.strip() 34 + except Exception as e: 35 + return str(e) 36 + 37 + def draw(self): 38 + h, w = self.s.getmaxyx() 39 + self.s.erase() 40 + 41 + # Header 42 + branch = self.git("branch", "--show-current") 43 + hdr = f" gitui | {branch} " 44 + self.s.attron(curses.color_pair(2) | curses.A_BOLD) 45 + self.s.addstr(0, 0, hdr.ljust(w)[:w-1]) 46 + self.s.attroff(curses.color_pair(2) | curses.A_BOLD) 47 + 48 + # Menu 49 + for i, item in enumerate(self.menu): 50 + if i == self.sel: 51 + self.s.attron(curses.color_pair(2) | curses.A_BOLD) 52 + self.s.addstr(2+i, 2, f"> {item}") 53 + self.s.attroff(curses.color_pair(2) | curses.A_BOLD) 54 + else: 55 + self.s.attron(curses.color_pair(7)) 56 + self.s.addstr(2+i, 2, f" {item}") 57 + self.s.attroff(curses.color_pair(7)) 58 + 59 + # Git status (right side) 60 + status = self.git("status", "-s") 61 + self.s.attron(curses.color_pair(3)) 62 + for i, line in enumerate(status.split("\n")[:8]): 63 + if line and 2+i < h-3: 64 + self.s.addstr(2+i, 20, line[:w-22]) 65 + self.s.attroff(curses.color_pair(3)) 66 + 67 + # Status message 68 + if self.msg: 69 + self.s.attron(curses.color_pair(5)) 70 + self.s.addstr(h-3, 2, self.msg[:w-4]) 71 + self.s.attroff(curses.color_pair(5)) 72 + 73 + # Input or help 74 + if self.input_mode: 75 + self.s.attron(curses.color_pair(5)) 76 + self.s.addstr(h-2, 2, f"{self.prompt}: {self.input}_") 77 + self.s.attroff(curses.color_pair(5)) 78 + else: 79 + self.s.attron(curses.color_pair(6)) 80 + self.s.addstr(h-1, 2, "j/k:nav | Enter:select | q:quit") 81 + self.s.attroff(curses.color_pair(6)) 82 + 83 + self.s.refresh() 84 + 85 + def commit(self): 86 + self.input_mode = True 87 + self.prompt = "Commit msg" 88 + self.input = "" 89 + self.cb = self._commit 90 + 91 + def _commit(self): 92 + r = self.git("commit", "-m", self.input) 93 + self.msg = "Committed!" if any(x in r for x in ["insertion", "file", "done"]) else r[:40] 94 + self.input_mode = False 95 + 96 + def push(self): 97 + r = self.git("push") 98 + self.msg = "Pushed!" if "up to date" in r.lower() or "done" in r.lower() else r[:40] 99 + 100 + def remote_add(self): 101 + self.input_mode = True 102 + self.prompt = "Remote name" 103 + self.input = "" 104 + self.cb = self._remote1 105 + 106 + def _remote1(self): 107 + self.fields = {"name": self.input} 108 + self.prompt = "Remote URL" 109 + self.input = "" 110 + self.cb = self._remote2 111 + 112 + def _remote2(self): 113 + r = self.git("remote", "add", self.fields["name"], self.input) 114 + self.msg = f"Remote {self.fields['name']} added" if not r else r[:40] 115 + self.input_mode = False 116 + 117 + def stash(self): 118 + self.input_mode = True 119 + self.prompt = "Stash msg (opt)" 120 + self.input = "" 121 + self.cb = self._stash 122 + 123 + def _stash(self): 124 + r = self.git("stash", "save", self.input) if self.input else self.git("stash") 125 + self.msg = "Stashed!" if "Saved" in r or not r.strip() else r[:40] 126 + self.input_mode = False 127 + 128 + def new_branch(self): 129 + self.input_mode = True 130 + self.prompt = "Branch name" 131 + self.input = "" 132 + self.cb = self._branch 133 + 134 + def _branch(self): 135 + r = self.git("checkout", "-b", self.input) 136 + self.msg = f"Branch {self.input} created" if not r.strip() else r[:40] 137 + self.input_mode = False 138 + 139 + def handle_key(self, k): 140 + if self.input_mode: 141 + if k == 27: 142 + self.input_mode = False 143 + elif k in (10, 13): 144 + if self.cb: self.cb() 145 + elif k in (127, 8, curses.KEY_BACKSPACE): 146 + self.input = self.input[:-1] 147 + elif 32 <= k < 127: 148 + self.input += chr(k) 149 + return 150 + 151 + if k == ord('q'): 152 + self.running = False 153 + elif k in (ord('j'), curses.KEY_DOWN): 154 + self.sel = min(self.sel + 1, len(self.menu) - 1) 155 + elif k in (ord('k'), curses.KEY_UP): 156 + self.sel = max(self.sel - 1, 0) 157 + elif k in (10, 13, ord('l')): 158 + action = self.menu[self.sel] 159 + {"Commit": self.commit, "Push": self.push, "Remote Add": self.remote_add, 160 + "Stash": self.stash, "New Branch": self.new_branch, "Quit": lambda: setattr(self, 'running', False)}[action]() 161 + 162 + def run(self): 163 + while self.running: 164 + self.draw() 165 + self.handle_key(self.s.getch()) 166 + 167 + if __name__ == "__main__": 168 + curses.wrapper(lambda s: GitUI(s).run())
+2
launch.sh
··· 1 + #!/bin/bash 2 + python3 "$(dirname "$0")/gitui.py"