diff --git a/briefing b/briefing index e9d0391..cdc1c00 100755 --- a/briefing +++ b/briefing @@ -528,25 +528,57 @@ def get_terminal_size(): def read_key(fd): - """Read a keypress from raw terminal. Returns key string.""" + """Read a keypress from raw terminal. Returns key string or ('CLICK', row, col) tuple.""" ch = os.read(fd, 1) if not ch: return '' if ch == b'\x1b': - # Escape sequence - seq = os.read(fd, 2) - if seq == b'[A': return 'UP' - if seq == b'[B': return 'DOWN' - if seq == b'[5': os.read(fd, 1); return 'PGUP' - if seq == b'[6': os.read(fd, 1); return 'PGDN' - if seq == b'[H': return 'HOME' - if seq == b'[F': return 'END' - if seq == b'[M': - # Mouse event: read 3 more bytes + # Read next bytes + b2 = os.read(fd, 1) + if b2 != b'[': + return 'ESC' + b3 = os.read(fd, 1) + + # SGR mouse: \033[= len(lines): + return None + # Strip ANSI to get visible text and map positions + raw = lines[line_idx] + clean = ANSI_ESC.sub('', raw) + # Find all #NNNN in the visible text + best = None + best_dist = 999 + for m in TASK_ID_RE.finditer(clean): + start, end = m.start(), m.end() + # Distance from click to the match + if start <= col <= end: + return int(m.group(1)) + dist = min(abs(col - start), abs(col - end)) + if dist < best_dist and dist <= 5: + best_dist = dist + best = int(m.group(1)) + return best + def split_lines(content): return content.split('\n') @@ -686,6 +742,15 @@ def tui_viewer(range_days, client): input_buf += key continue + # Mouse click — find task number + if isinstance(key, tuple) and key[0] == 'CLICK': + _, click_row, click_col = key + task_id = find_task_at_click(click_row, click_col) + if task_id: + viewing_task = True + load_task(task_id) + continue + if key == 'q': break elif key == 'r':