import argparse import datetime as dt import json import math import os from pathlib import Path import sys import time try: import msvcrt except ImportError: msvcrt = None APP_NAME = "Tiimeer.com" APP_CREDIT = "Developed by Wox4us, Co | https://wox4us.com/" DATA_FILE = Path(__file__).with_name("brew_state.json") DEFAULT_FOCUS_MINUTES = 25 DEFAULT_REFILL_MINUTES = 5 DEFAULT_EXTEND_MINUTES = 5 FRAME_DELAY = 0.15 PANEL_CONTENT_WIDTH = 50 ANSI_ENABLED = False STATUS_ON_DECK = "On deck" STATUS_BREWING = "Brewing..." STATUS_COOL = "Let it cool" STATUS_SERVED = "Served" MODE_FOCUS = "focus" MODE_REFILL = "refill" RESET = "\033[0m" BOLD = "\033[1m" DIM = "\033[2m" WHITE = "\033[38;5;255m" SILVER = "\033[38;5;250m" SKY = "\033[38;5;117m" COCOA = "\033[38;5;94m" STEAM_FRAMES = [ (" ( )", " ( )", " ( )"), (" () ", " ( )", " () "), (" /\\ ", " /\\ ", " /\\ "), (" ~~ ", " ~~~~ ", " ~~ "), ] PROFILE_STYLES = { "Espresso": { "accent_color": SKY, "coffee_color": COCOA, "foam_color": WHITE, "coffee_char": "#", "foam_char": ".", "width": 10, "height": 5, }, } def paint(text, *codes): """Apply ANSI styling when available.""" if not ANSI_ENABLED or not codes: return text return "".join(codes) + text + RESET def enable_windows_ansi(): """Enable ANSI escape handling when the console supports it.""" global ANSI_ENABLED if os.name != "nt": ANSI_ENABLED = True return try: import ctypes kernel32 = ctypes.windll.kernel32 handle = kernel32.GetStdHandle(-11) mode = ctypes.c_uint32() if kernel32.GetConsoleMode(handle, ctypes.byref(mode)): ANSI_ENABLED = bool(kernel32.SetConsoleMode(handle, mode.value | 0x0004)) except Exception: ANSI_ENABLED = False def clear_screen(): """Clear the terminal.""" if ANSI_ENABLED: sys.stdout.write("\033[2J\033[H") sys.stdout.flush() return os.system("cls" if os.name == "nt" else "clear") def move_cursor_home(): """Move the cursor to the top-left corner.""" if ANSI_ENABLED: sys.stdout.write("\033[H") return if os.name != "nt": return try: import ctypes class Coord(ctypes.Structure): _fields_ = [("X", ctypes.c_short), ("Y", ctypes.c_short)] handle = ctypes.windll.kernel32.GetStdHandle(-11) ctypes.windll.kernel32.SetConsoleCursorPosition(handle, Coord(0, 0)) except Exception: clear_screen() def hide_cursor(): """Hide the cursor while the timer is running.""" if ANSI_ENABLED: sys.stdout.write("\033[?25l") sys.stdout.flush() def show_cursor(): """Restore the cursor before exit.""" if ANSI_ENABLED: sys.stdout.write("\033[?25h") sys.stdout.flush() def now_local(): return dt.datetime.now() def today_key(): return now_local().date().isoformat() def timestamp(): return now_local().isoformat(timespec="seconds") def new_state(): return { "next_order_id": 1, "orders": [], "daily_activity": {}, } def load_state(): if not DATA_FILE.exists(): return new_state() try: state = json.loads(DATA_FILE.read_text(encoding="utf-8")) except (OSError, json.JSONDecodeError): return new_state() state.setdefault("next_order_id", 1) state.setdefault("orders", []) state.setdefault("daily_activity", {}) return state def save_state(state): DATA_FILE.write_text(json.dumps(state, indent=2), encoding="utf-8") def ensure_daily_entry(state, day): entry = state["daily_activity"].setdefault(day, {}) entry.setdefault("cups_served", 0) entry.setdefault("brews_completed", 0) return entry def bump_daily_activity(state, cups_served=0, brews_completed=0): entry = ensure_daily_entry(state, today_key()) entry["cups_served"] += cups_served entry["brews_completed"] += brews_completed def strong_roast_streak(state): streak = 0 day = now_local().date() while True: entry = state["daily_activity"].get(day.isoformat()) if not entry: break if entry.get("cups_served", 0) <= 0 and entry.get("brews_completed", 0) <= 0: break streak += 1 day -= dt.timedelta(days=1) return streak def find_order(state, order_id): for order in state["orders"]: if order["id"] == order_id: return order return None def status_rank(status): ranks = { STATUS_BREWING: 0, STATUS_COOL: 1, STATUS_ON_DECK: 2, STATUS_SERVED: 3, } return ranks.get(status, 9) def sorted_orders(orders): return sorted(orders, key=lambda order: (status_rank(order["status"]), order["id"])) def truncate(text, width): if len(text) <= width: return text if width <= 3: return text[:width] return text[: width - 3] + "..." def format_clock(seconds): minutes = seconds // 60 remaining_seconds = seconds % 60 return f"{minutes:02d}:{remaining_seconds:02d}" def format_focus_total(total_seconds): minutes = total_seconds // 60 hours, minutes = divmod(minutes, 60) if hours and minutes: return f"{hours}h {minutes}m" if hours: return f"{hours}h" return f"{minutes}m" def make_progress_bar(remaining, total, width=22): if total <= 0: return "[" + ("=" * width) + "] 100%" complete_ratio = 1 - (remaining / total) filled = max(0, min(width, int(round(complete_ratio * width)))) bar = "[" + ("=" * filled) + ("-" * (width - filled)) + "]" percent = int(round(complete_ratio * 100)) return f"{bar} {percent:>3d}%" def panel_border(left, middle, right, color_code): return paint(f" {left}{middle * (PANEL_CONTENT_WIDTH + 2)}{right}", color_code) def panel_line(label, value, color_code): content = f"{label:<12} {value}" content = content[:PANEL_CONTENT_WIDTH] return paint(f" | {content:<{PANEL_CONTENT_WIDTH}} |", color_code) def recent_served_orders(state, limit=4): if not state: return [] served_orders = [order for order in state["orders"] if order["status"] == STATUS_SERVED] served_orders.sort( key=lambda order: order.get("completed_at") or order.get("updated_at") or "", reverse=True, ) if limit is None: return served_orders return served_orders[:limit] def build_fill_lines(style, fill_ratio, bubble_phase): height = style["height"] width = style["width"] filled_rows = max(0, min(height, int(math.ceil(fill_ratio * height)))) lines = [] for row_index in range(height): row_from_bottom = height - row_index if row_from_bottom <= filled_rows: if row_from_bottom == filled_rows: chars = [] for column in range(width): if (column + bubble_phase) % 5 == 0: chars.append(style["foam_char"]) else: chars.append(style["coffee_char"]) content = "".join(chars) else: content = style["coffee_char"] * width else: content = " " * width lines.append(content) return lines def color_fill(fill_text, style): colored = [] for character in fill_text: if character == style["foam_char"]: colored.append(paint(character, style["foam_color"], BOLD)) elif character == style["coffee_char"]: colored.append(paint(character, style["coffee_color"])) else: colored.append(character) return "".join(colored) def build_mug_lines(style, fill_lines): width = style["width"] border = "-" * width handle_top = "----." handle_mid = " |" handle_bottom = "----'" indent = " " return [ {"prefix": indent, "text": "." + border + "."}, {"prefix": indent + "|", "fill": fill_lines[0], "suffix": "|" + handle_top}, {"prefix": indent + "|", "fill": fill_lines[1], "suffix": "|" + handle_mid}, {"prefix": indent + "|", "fill": fill_lines[2], "suffix": "|" + handle_mid}, {"prefix": indent + "|", "fill": fill_lines[3], "suffix": "|" + handle_mid}, {"prefix": indent + "|", "fill": fill_lines[4], "suffix": "|" + handle_bottom}, {"prefix": indent, "text": "'" + border + "'"}, ] def render_timer_screen(order, state, mode, remaining, total, frame_index, paused, show_history, action_note): profile = "Espresso" style = PROFILE_STYLES[profile] fill_ratio = remaining / total if total else 0 steam = STEAM_FRAMES[frame_index % len(STEAM_FRAMES)] fill_lines = build_fill_lines(style, fill_ratio, bubble_phase=frame_index % 6) mug_lines = build_mug_lines(style, fill_lines) if mode == MODE_FOCUS: session_label = "Focus Session" headline = "Focus session running." if not paused else "Timer paused." state_label = STATUS_COOL if paused else STATUS_BREWING else: session_label = "5-Minute Break" headline = "Take a short break." if not paused else "Break paused." state_label = STATUS_COOL if paused else "Refill" if action_note: headline = action_note lines = [ "", paint(f" {APP_NAME}", style["accent_color"], BOLD), paint(f" {APP_CREDIT}", SILVER, DIM), "", paint(steam[0], SILVER, DIM), paint(steam[1], SILVER, DIM), paint(steam[2], SILVER, DIM), "", ] for line in mug_lines: if "fill" in line: fill = color_fill(line["fill"], style) lines.append(paint(line["prefix"], SILVER) + fill + paint(line["suffix"], SILVER)) else: lines.append(paint(line["prefix"] + line["text"], SILVER)) lines.append("") lines.append(panel_border(".", "-", ".", SILVER)) lines.append(panel_line("Current Task", order["title"] if order else "Break", SILVER)) lines.append(panel_line("Theme", profile, SILVER)) lines.append(panel_line("Mode", session_label, SILVER)) lines.append(panel_line("State", state_label, SILVER)) lines.append(panel_line("Time Left", format_clock(remaining), SILVER)) lines.append(panel_line("Progress", make_progress_bar(remaining, total), SILVER)) lines.append(panel_line("Note", headline, SILVER)) lines.append(panel_border("'", "-", "'", SILVER)) lines.append("") if msvcrt: if mode == MODE_FOCUS: control_text = " Controls: P pause/resume | E extend +5m | B 5-min break | D done | H history | Q quit" else: control_text = " Controls: P pause/resume | H history | Q end break" lines.append(paint(control_text, SILVER, DIM)) else: lines.append(paint(" Press Ctrl+C to stop the current timer.", SILVER, DIM)) lines.append("") if show_history: served_orders = recent_served_orders(state) served_total = len(recent_served_orders(state, limit=None)) lines.append(panel_border(".", "-", ".", SILVER)) if served_orders: lines.append(panel_line("Served", f"{served_total} total", SILVER)) for served_order in served_orders: lines.append(panel_line(f"#{served_order['id']}", served_order["title"], SILVER)) else: lines.append(panel_line("Served", "No completed orders yet", SILVER)) lines.append(panel_border("'", "-", "'", SILVER)) lines.append("") return "\n".join(lines) def poll_key(): if not msvcrt or not msvcrt.kbhit(): return None key = msvcrt.getwch() if key in ("\x00", "\xe0"): msvcrt.getwch() return None return key.lower() def set_order_status(state, order, status): order["status"] = status order["updated_at"] = timestamp() save_state(state) def run_timer(order, state, duration_seconds, mode): clear_screen() hide_cursor() total_seconds = float(duration_seconds) remaining = float(duration_seconds) last_tick = time.monotonic() paused = False frame_index = 0 show_history = False action_note = "" try: while True: key = poll_key() if key == "p": paused = not paused last_tick = time.monotonic() if order and mode == MODE_FOCUS: new_status = STATUS_COOL if paused else STATUS_BREWING set_order_status(state, order, new_status) action_note = "Session paused." if paused else "Session resumed." elif key == "e" and order and mode == MODE_FOCUS: extra_seconds = int(DEFAULT_EXTEND_MINUTES * 60) remaining += extra_seconds total_seconds += extra_seconds action_note = f"Added {DEFAULT_EXTEND_MINUTES} extra minutes." elif key == "b" and order and mode == MODE_FOCUS: was_paused = paused paused = True if state is not None: set_order_status(state, order, STATUS_COOL) clear_screen() break_result = run_timer( order=None, state=state, duration_seconds=int(DEFAULT_REFILL_MINUTES * 60), mode=MODE_REFILL, ) clear_screen() hide_cursor() paused = was_paused last_tick = time.monotonic() if state is not None: set_order_status(state, order, STATUS_COOL if paused else STATUS_BREWING) if break_result["completed"]: action_note = "Break finished. Session resumed." else: action_note = "Break ended early." elif key == "d" and order and mode == MODE_FOCUS: elapsed_seconds = max(0, int(round(total_seconds - max(remaining, 0)))) return {"completed": False, "stopped": False, "done": True, "elapsed_seconds": elapsed_seconds} elif key == "h" and state is not None: show_history = not show_history clear_screen() action_note = "Showing completed orders." if show_history else "" elif key == "q": elapsed_seconds = max(0, int(round(total_seconds - max(remaining, 0)))) return {"completed": False, "stopped": True, "done": False, "elapsed_seconds": elapsed_seconds} now = time.monotonic() if not paused: remaining -= now - last_tick frame_index += 1 last_tick = now display_remaining = max(0, int(math.ceil(remaining))) move_cursor_home() sys.stdout.write( render_timer_screen( order, state, mode, display_remaining, int(math.ceil(total_seconds)), frame_index, paused, show_history, action_note, ) ) sys.stdout.flush() if remaining <= 0: return { "completed": True, "stopped": False, "done": False, "elapsed_seconds": int(math.ceil(total_seconds)), } time.sleep(FRAME_DELAY) finally: show_cursor() def print_banner(): print(paint(APP_NAME, SKY, BOLD)) print(paint(APP_CREDIT, SILVER)) print(paint("Terminal focus sessions with a coffee-timer interface.", DIM)) print("-" * 60) def print_help_examples(): print("Quick start:") print(" tiimeer") print("") print("Commands:") print(" add Add a New Order") print(" list View Today's Brew") print(" history View completed orders") print(" start Start a Brew Session for an order") print(" done Mark an order as Done") print(" serve Mark an order as Served") print(" delete Remove an order") print(" stats Show cafe progress") print("") print("Examples:") print(" tiimeer add Write landing page") print(" tiimeer list --all") print(" tiimeer history") print(" tiimeer start 2 --minutes 25 --refill 5") print(" tiimeer done 2") def print_stats_block(state): today = ensure_daily_entry(state, today_key()) print("Session Stats") print(f" Cups served today : {today['cups_served']}") print(f" Brews completed : {today['brews_completed']}") print(f" Strong roast streak : {strong_roast_streak(state)} day(s)") def print_orders(orders): if not orders: print("No orders yet.") print("Add a New Order to start Today's Brew.") return id_width = 4 title_width = 32 status_width = 15 brews_width = 7 focus_width = 8 header = ( f"{'ID':<{id_width}} " f"{'Order':<{title_width}} " f"{'State':<{status_width}} " f"{'Brews':<{brews_width}} " f"{'Focus':<{focus_width}}" ) print(header) print("-" * len(header)) for order in orders: row = ( f"{order['id']:<{id_width}} " f"{truncate(order['title'], title_width):<{title_width}} " f"{order['status']:<{status_width}} " f"{order['brews_completed']:<{brews_width}} " f"{format_focus_total(order['total_focus_seconds']):<{focus_width}}" ) print(row) def ask_yes_no(prompt, default=False): suffix = "[Y/n]" if default else "[y/N]" answer = input(f"{prompt} {suffix}: ").strip().lower() if not answer: return default return answer in {"y", "yes"} def add_order(state, title): order = { "id": state["next_order_id"], "title": title, "profile": "Espresso", "status": STATUS_ON_DECK, "created_at": timestamp(), "updated_at": timestamp(), "completed_at": None, "brews_completed": 0, "total_focus_seconds": 0, } state["orders"].append(order) state["next_order_id"] += 1 save_state(state) return order def serve_order(state, order, announce=True): if order["status"] == STATUS_SERVED: if announce: print("That order has already been served.") return False order["status"] = STATUS_SERVED order["completed_at"] = timestamp() order["updated_at"] = timestamp() bump_daily_activity(state, cups_served=1) save_state(state) if announce: print(f"Completed: #{order['id']} {order['title']}") return True def delete_order(state, order): state["orders"] = [item for item in state["orders"] if item["id"] != order["id"]] save_state(state) def command_add(args, state): title = " ".join(args.title).strip() order = add_order(state, title) print_banner() print(f"Added: #{order['id']} {order['title']}") print("Timer theme: Espresso") def command_list(args, state): print_banner() print("Open Orders") print("") orders = sorted_orders(state["orders"]) if not args.all: orders = [order for order in orders if order["status"] != STATUS_SERVED] print_orders(orders) def command_history(args, state): del args print_banner() print("Served Orders") print("") orders = [order for order in sorted_orders(state["orders"]) if order["status"] == STATUS_SERVED] print_orders(orders) def command_serve(args, state): order = find_order(state, args.order_id) print_banner() if not order: print(f"Order #{args.order_id} was not found.") return serve_order(state, order) def command_done(args, state): order = find_order(state, args.order_id) print_banner() if not order: print(f"Order #{args.order_id} was not found.") return if serve_order(state, order, announce=False): print(f"Done. #{order['id']} {order['title']} has been completed.") else: print("That order has already been marked as done.") def command_delete(args, state): order = find_order(state, args.order_id) print_banner() if not order: print(f"Order #{args.order_id} was not found.") return if not args.yes and not ask_yes_no(f"Delete order #{order['id']} '{order['title']}'?", default=False): print("Order kept on the menu.") return delete_order(state, order) print(f"Removed order #{order['id']}: {order['title']}") def run_refill_timer(minutes): refill_seconds = max(1, int(round(minutes * 60))) try: result = run_timer(order=None, state=None, duration_seconds=refill_seconds, mode=MODE_REFILL) except KeyboardInterrupt: clear_screen() print_banner() print("Refill ended early.") return clear_screen() print_banner() if result["completed"]: print("Take a sip, you've earned it.") else: print("Refill ended early.") def start_order_session(state, order, focus_minutes, refill_minutes, skip_refill=False): focus_seconds = max(1, int(round(focus_minutes * 60))) set_order_status(state, order, STATUS_BREWING) try: result = run_timer(order=order, state=state, duration_seconds=focus_seconds, mode=MODE_FOCUS) except KeyboardInterrupt: set_order_status(state, order, STATUS_COOL) clear_screen() print_banner() print("Brew Session stopped.") print("The order has been set to Let it cool.") return "cooled" clear_screen() print_banner() if result["completed"]: completed_seconds = max(0, result.get("elapsed_seconds", focus_seconds)) order["brews_completed"] += 1 order["total_focus_seconds"] += completed_seconds order["updated_at"] = timestamp() order["status"] = STATUS_ON_DECK bump_daily_activity(state, brews_completed=1) save_state(state) print("Session complete.") print("This task is ready to be marked done.") if ask_yes_no("Mark this order as Done now?", default=False): serve_order(state, order, announce=False) print("Task marked done.") session_outcome = "served" else: print("Task returned to the active list.") session_outcome = "pending" if refill_minutes > 0 and not skip_refill: if ask_yes_no("Start a Refill timer now?", default=True): run_refill_timer(refill_minutes) else: print("Refill skipped.") return session_outcome elif result["done"]: elapsed_seconds = max(0, result.get("elapsed_seconds", 0)) order["brews_completed"] += 1 order["total_focus_seconds"] += elapsed_seconds order["updated_at"] = timestamp() bump_daily_activity(state, brews_completed=1) save_state(state) serve_order(state, order, announce=False) print("Done. The task has been completed.") if refill_minutes > 0 and not skip_refill: if ask_yes_no("Start a Refill timer now?", default=True): run_refill_timer(refill_minutes) else: print("Refill skipped.") return "served" else: set_order_status(state, order, STATUS_COOL) print("Brew Session stopped.") print("The order has been set to Let it cool.") return "cooled" def command_start(args, state): order = find_order(state, args.order_id) print_banner() if not order: print(f"Order #{args.order_id} was not found.") return if order["status"] == STATUS_SERVED: print("That order has already been served.") print("Use 'list --all' if you want to review past orders.") return refill_minutes = max(0, args.refill) start_order_session(state, order, args.minutes, refill_minutes, skip_refill=args.skip_refill) def command_stats(args, state): del args print_banner() print_stats_block(state) def command_dashboard(args, state): del args print_banner() print_stats_block(state) print("") print("Open Orders") print("") open_orders = [order for order in sorted_orders(state["orders"]) if order["status"] != STATUS_SERVED] print_orders(open_orders) print("") print_help_examples() def prompt_minutes(prompt, default_minutes): while True: raw = input(f"{prompt} [{default_minutes}]: ").strip().replace(",", ".") if not raw: return float(default_minutes) try: minutes = float(raw) if minutes > 0: return minutes except ValueError: pass print("Enter a valid number of minutes, for example 25 or 12.5.") def quick_brew_flow(state): prompt = "What would you like to do? " first_prompt = True while True: print_banner() title = input(prompt).strip() if not title: if first_prompt: print("No order was added.") else: print("No new order was added.") return minutes = prompt_minutes("How long would you like to focus on it (in minutes)?", DEFAULT_FOCUS_MINUTES) order = add_order(state, title) print("") print(f"Order #{order['id']} is ready. Starting the timer...") time.sleep(1) outcome = start_order_session(state, order, minutes, DEFAULT_REFILL_MINUTES, skip_refill=False) if outcome != "served": return prompt = "What would you like to do next? " first_prompt = False def build_parser(): parser = argparse.ArgumentParser( prog="brew", description="A cozy coffee-themed productivity CLI for orders and focus sessions.", ) subparsers = parser.add_subparsers(dest="command") add_parser = subparsers.add_parser("add", help="Add a New Order") add_parser.add_argument("title", nargs="+", help="Order title") list_parser = subparsers.add_parser("list", help="View Today's Brew") list_parser.add_argument("--all", action="store_true", help="Include Served orders") subparsers.add_parser("history", help="View completed orders") start_parser = subparsers.add_parser("start", help="Start a Brew Session for an order") start_parser.add_argument("order_id", type=int, help="Order ID") start_parser.add_argument("--minutes", type=float, default=DEFAULT_FOCUS_MINUTES, help="Focus time in minutes") start_parser.add_argument("--refill", type=float, default=DEFAULT_REFILL_MINUTES, help="Refill time in minutes") start_parser.add_argument("--skip-refill", action="store_true", help="Skip the refill prompt") done_parser = subparsers.add_parser("done", help="Mark an order as Done") done_parser.add_argument("order_id", type=int, help="Order ID") serve_parser = subparsers.add_parser("serve", help="Mark an order as Served") serve_parser.add_argument("order_id", type=int, help="Order ID") delete_parser = subparsers.add_parser("delete", help="Remove an order") delete_parser.add_argument("order_id", type=int, help="Order ID") delete_parser.add_argument("-y", "--yes", action="store_true", help="Delete without prompting") subparsers.add_parser("stats", help="Show cafe progress") subparsers.add_parser("dashboard", help="Show Today's Brew and cafe stats") return parser def dispatch(args, state): commands = { "add": command_add, "list": command_list, "history": command_history, "start": command_start, "done": command_done, "serve": command_serve, "delete": command_delete, "stats": command_stats, "dashboard": command_dashboard, } command = commands.get(args.command, command_dashboard) command(args, state) def main(argv=None): enable_windows_ansi() if argv is None: argv = sys.argv[1:] if argv and argv[0].lower() == "brew": argv = argv[1:] if not argv: state = load_state() quick_brew_flow(state) return parser = build_parser() try: args = parser.parse_args(argv) if hasattr(args, "minutes") and args.minutes <= 0: parser.error("--minutes must be greater than 0.") if hasattr(args, "refill") and args.refill < 0: parser.error("--refill cannot be negative.") except ValueError as error: parser.error(str(error)) state = load_state() dispatch(args, state) if __name__ == "__main__": main()