Files
box/bin/hatch_menu/tabs/general.py
T

96 lines
2.9 KiB
Python
Raw Normal View History

"""General tab: usage balances, theme picker, redeem entrypoint.
Usage parsing moved here from invite.py (single copy). All flows are
ws-level: sessions and error shaping live in toggles.py.
"""
import re
from approvals import cdp_evaluate
from hatch_menu import controls, dialog
TAB = "General"
THEME_VALUES = ("match", "default", "blue", "purple", "pink",
"orange", "green", "beige", "monochrome")
_FREE_RE = re.compile(r"\bfree plan\b", re.IGNORECASE)
_PCT_RE = re.compile(r"(\d+)%\s*used")
_RESET_RE = re.compile(r"resets?\s+on\s+([A-Z][a-z]+\s+\d{1,2})",
re.IGNORECASE)
_TOK_RE = re.compile(r"\(([0-9.,]+\s*[BMK]?)\s*tokens?\s+left\)",
re.IGNORECASE)
def parse_usage_text(text):
"""Parse a General-tab usage block into a balance dict.
Returns None for empty/unreadable text. Weekly fields stay None
when the block only carries additional-tokens rows.
"""
if not text or not text.strip():
return None
out = {"plan": None, "weekly": {"pct_used": None, "resets_on": None},
"additional": {"pct_used": None, "tokens_left": None,
"never_expires": False}}
if _FREE_RE.search(text):
out["plan"] = "free"
pcts = _PCT_RE.findall(text)
if pcts:
out["weekly"]["pct_used"] = int(pcts[0])
if len(pcts) > 1:
out["additional"]["pct_used"] = int(pcts[1])
m = _RESET_RE.search(text)
if m:
out["weekly"]["resets_on"] = m.group(1)
m = _TOK_RE.search(text)
if m:
out["additional"]["tokens_left"] = m.group(1).strip()
if "never expires" in text.lower():
out["additional"]["never_expires"] = True
return out
def _eval(ws, js, timeout=8.0):
try:
return cdp_evaluate(ws, js, timeout=timeout)
except Exception:
return None
def usage(ws):
"""Usage balances from General tab. Dict, or None when unreadable."""
if not dialog.goto_tab(ws, TAB):
return None
return parse_usage_text(dialog.dialog_text(ws))
def theme(ws):
"""Current theme radio value, or None when unreadable."""
if not dialog.goto_tab(ws, TAB):
return None
for r in controls.list_radios(ws):
if r.get("checked"):
return r.get("value")
return None
def set_theme(ws, value):
"""Set theme by aria-label; verify-then-fallback. Bool."""
if not dialog.goto_tab(ws, TAB):
return False
return controls.set_radio_by_aria(ws, value)
def redeem_row(ws):
"""True when the 'Redeem invite code' row is present (eligible)."""
if not dialog.goto_tab(ws, TAB):
return False
text = (dialog.dialog_text(ws) or "").lower()
return "redeem" in text and "invite code" in text
def describe(ws):
"""Full General inventory: usage, theme, redeem entrypoint."""
return {"usage": usage(ws), "theme": theme(ws),
"redeem_row": redeem_row(ws)}