From d78b6d188eb9ba206c8769b41c8021e7ba52ef98 Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Fri, 26 Feb 2021 20:23:46 -0600 Subject: Add beta TUI client --- README.md | 15 +++++++++ main.py | 32 ++++++++++++------- ncurses.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 12 deletions(-) create mode 100755 ncurses.py diff --git a/README.md b/README.md index 23ee894..8dfd022 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,21 @@ example, if your wiki is at `wiki.example.com`, the url field should be set to Run `main.py` in order to use the program. +Example uses: +- Create a page `./main.py create 2021/02/26 "February 26"` +- Edit a page `./main.py edit 2021/02/26` + +## Ncurses TUI +A ncurses TUI beta client also is provided, though it is still a work in +progress. It is run through the file `ncurses.py` + +Keys: +- `j`/down: move down +- `k`/up: move up +- `t`: create/edit page for today +- `c`: create a page +- `e`: edit page selected +- `q`: quit ## TODO - Include options to always answer yes or no diff --git a/main.py b/main.py index 32ace0e..5e25498 100755 --- a/main.py +++ b/main.py @@ -20,14 +20,15 @@ def today(args): args: used """ - today = datetime.datetime.now() - path = today.strftime("journal/%Y/%b/%d").lower() + print(args) + t = datetime.datetime.now() + path = t.strftime("journal/%Y/%b/%d").lower() if get_single_page(path) is not None: edit({"path": path}) else: - date_int = int(today.strftime("%d")) - title = today.strftime("%B ") + str(date_int) - create([path, title]) + date_int = int(t.strftime("%d")) + title = t.strftime("%B ") + str(date_int) + create({"path": path, "title": title}) def create(args): """ @@ -46,7 +47,7 @@ def create(args): return title = args["title"] path = args["path"] - if "content" in args: + if "content" not in args: content = open_editor("create", path, "") else: content = args["content"] @@ -57,6 +58,16 @@ def create(args): sys.exit(1) print(result["message"]) +def get_tree(regex): + response = graphql_queries.get_tree() + regex = " ".join(regex) + pages = [] + for item in response["data"]["pages"]["list"]: + if not re.search(regex, item["path"]): + continue + pages.append(item) + return pages + def tree(args): """ Finds pages based on a path search @@ -64,11 +75,7 @@ def tree(args): args is a dictionary with the following keys: regex: the regex to search paths with """ - response = graphql_queries.get_tree() - regex = " ".join(args["regex"]) - for item in response["data"]["pages"]["list"]: - if not re.search(regex, item["path"]): - continue + for item in get_tree(args["regex"]): print_item(item) def get_single_page(path): @@ -154,6 +161,7 @@ def edit(args): args is a dictionary with the following keys: path: the path of the page to edit + save (optional): include with any value to save without any prompt """ page = get_single_page(args["path"]) if page is None: @@ -172,7 +180,7 @@ def edit(args): print("-" * 80) print(new_body) print("-" * 80) - if input("Save changes? (y/n) ") == "y": + if "save" in args or input("Save changes? (y/n) ") == "y": response = graphql_queries.edit_page(page["id"], new_body, page["title"], page["path"]) result = response["data"]["pages"]["update"]["responseResult"] if not result["succeeded"]: diff --git a/ncurses.py b/ncurses.py new file mode 100755 index 0000000..073ad50 --- /dev/null +++ b/ncurses.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +import main +import curses +from curses import wrapper + +def pager(stdscr, lst): + ''' + Runs a pager for each string item in lst + ''' + cols = stdscr.getmaxyx()[1] + rows = stdscr.getmaxyx()[0] + offset = 0 + selected = 0 + while True: + stdscr.clear() + for i in range(min(rows-1, len(lst))): + x = lst[i+offset] + if i+offset == selected: + stdscr.addstr(i, 0, x[:cols], curses.A_UNDERLINE) + else: + stdscr.addstr(i, 0, x[:cols]) + if offset == 0: + stdscr.addstr(rows-1, 0, "--top--", curses.A_REVERSE) + elif offset + rows <= len(lst): + stdscr.addstr(rows-1, 0, "--more--", curses.A_REVERSE) + else: + stdscr.addstr(rows-1, 0, "--end--", curses.A_REVERSE) + k = stdscr.getch() + if k == curses.KEY_DOWN or k == ord('j'): + selected = min(len(lst), selected+1) + if (selected - offset) > (2 * rows / 3): + offset = min(len(lst)-rows+1, offset+1) + elif k == curses.KEY_UP or k == ord('k'): + selected = max(0, selected-1) + if (selected - offset) < (rows / 3): + offset = max(0, offset-1) + elif k == curses.KEY_NPAGE: + offset = min(len(lst)-rows+1, offset+rows-2) + selected = min(len(lst)-rows+1, selected+rows-2) + elif k == curses.KEY_PPAGE: + offset = max(0, offset-rows+2) + selected = max(0, selected-rows+2) + elif k == curses.KEY_HOME: + offset = 0 + selected = 0 + elif k == curses.KEY_END: + offset = len(lst)-rows+1 + selected = len(lst)-1 + elif k == curses.KEY_ENTER or k == 10: + return {"index": selected, "action": "select"} + elif k == ord('q'): + return {"index": selected, "action": "quit"} + elif k == ord('e'): + return {"index": selected, "action": "edit"} + elif k == ord('c'): + return {"index": selected, "action": "create"} + elif k == ord('t'): + return {"index": selected, "action": "today"} + stdscr.refresh() + +def enter_value(stdscr, prefix, row): + """ + Creates a prompt to enter a value on the given row + """ + title = "" + stdscr.addstr(row,0, prefix + title) + k = stdscr.getch() + while k != 10 and k != curses.KEY_ENTER: + if k in (curses.KEY_BACKSPACE, '\b', '\x7f'): + if len(title) > 0: + title = title[:-1] + else: + title += chr(k) + stdscr.deleteln() + stdscr.addstr(row,0, prefix + title) + k = stdscr.getch() + return title + +def m(stdscr): + """ + The main method for the ncurses wrapper + """ + items = main.get_tree("") + while True: + ret = pager(stdscr, [x["path"] + "\t" + x["title"] for x in items]) + if ret["action"] == "select": + selected = items[ret["index"]] + ret = pager(stdscr, main.get_single_page(selected["path"])["content"].split("\n")) + elif ret["action"] == "edit": + selected = items[ret["index"]] + main.edit({"path":selected["path"], "save": True}) + elif ret["action"] == "create": + stdscr.clear() + title = enter_value(stdscr, "Enter title: ", 0) + path = enter_value(stdscr, "Enter path: ", 1) + main.create({"path": path, "title": title}) + elif ret["action"] == "today": + main.today({}) + else: + break + +# Run the ncurses wrapper +try: + wrapper(m) +except Exception as e: + raise e -- cgit v1.2.3