retronews

a featureful fork of the luke8086/retronews hn+lobste.rs tui
Log | Files | Refs | README | LICENSE

commit 8930fbe5e28e2d667c9eab0958a0c1cdb066d68e
parent 74965a47eda62492442a91f58f82eb61db7a1ebb
Author: quantumish <freifeld.david@gmail.com>
Date:   Wed,  8 Jul 2026 01:07:56 +0100

Generalize row display formatting

Diffstat:
Mretronews.py | 56+++++++++++++++++++++++++++++++++++---------------------
1 file changed, 35 insertions(+), 21 deletions(-)

diff --git a/retronews.py b/retronews.py @@ -142,6 +142,13 @@ COLORS: dict[Color, tuple[int, int]] = { "url": (curses.COLOR_MAGENTA, -1), } +UNREAD_SIZE = 3 +UNREAD_MANY_CHAR = '!' +AUTHOR_SIZE = 10 +COLUMN_SPACING = 2 +DATECOL_SIZE = 16 +DATECOL_FUNC = lambda date: date.strftime("%Y-%m-%d %H:%M") + REQUEST_TIMEOUT = 10 # Recognize ">text", "> text", ">>text", ">> text", etc. @@ -169,6 +176,14 @@ else: T = TypeVar("T") +def truncate_ellipsis(string: str, length: int) -> str: + if len(string) > length: + return string[:length-1] + "…" + else: + return string + +TRUNCATE = truncate_ellipsis + class ExitException(Exception): code: int message: str @@ -846,20 +861,13 @@ def cmd_load_page(app: AppState) -> None: def lb_group(name: str, path: str): return Group(label=name, fetch=lambda db, page: lb_fetch_threads(path, page)) -MAX_DISPLAY_LENGTH = 25 -def truncate_visible(string: str): - if len(string) > MAX_DISPLAY_LENGTH: - return string[:MAX_DISPLAY_LENGTH] + "..." - else: - return string - def cmd_lb_see_tags(app: AppState) -> None: user_input = app_prompt(app, "Stories for tag(s) (comma to combine): ") - app_load_group(app, lb_group(truncate_visible(user_input), "t/"+user_input)) + app_load_group(app, lb_group(TRUNCATE(user_input, 25), "t/"+user_input)) def cmd_lb_see_user(app: AppState) -> None: user_input = app_prompt(app, "Stories posted by user: ") - app_load_group(app, lb_group(truncate_visible(user_input), f"~{user_input}/stories")) + app_load_group(app, lb_group(TRUNCATE(user_input, 25), f"~{user_input}/stories")) def cmd_open(app: AppState) -> None: if (msg := app.selected_message) is None: @@ -1435,22 +1443,26 @@ def app_prompt(app: AppState, prompt: str) -> str: return ret +def app_chgat(app: AppState, row, start, size, color) -> int: + app.screen.chgat(row, start, size, color) + return start + size def app_render_index_row(app: AppState, row: int, message: Message) -> None: cols = app.layout.cols - date = message.date.strftime("%m-%d %H:%M") - author = (message.author or "<unknown>")[:10].ljust(10) + date = TRUNCATE(DATECOL_FUNC(message.date), DATECOL_SIZE).rjust(DATECOL_SIZE) + author = TRUNCATE(message.author or "<unknown>", AUTHOR_SIZE).ljust(AUTHOR_SIZE) is_response = message.title.startswith("Re:") and not message.is_thread is_selected = message == app.selected_message hide_title = is_response and row > app.layout.index_start and not message.flags.starred and not is_selected title = "" if hide_title else text_clean(message.title, ascii=app.ascii) - unread = ( - str(max(min(message.total_comments - message.read_comments, 9999), 0)).rjust(4) if message.is_thread else " " - ) + unread_count = max(message.total_comments - message.read_comments, 0) + unread_repr = str(unread_count) if unread_count < (10**UNREAD_SIZE - 1) else (UNREAD_MANY_CHAR * UNREAD_SIZE) + unread = unread_repr.rjust(UNREAD_SIZE) if message.is_thread else (' ' * UNREAD_SIZE) - app.screen.insstr(row, 0, f"[{date}] [{author}] [{unread}] {message.index_tree}{title}") + spacing = ' ' * COLUMN_SPACING + app.screen.insstr(row, 0, f"[{date}]{spacing}[{author}]{spacing}[{unread}]{spacing}{message.index_tree}{title}") if is_selected: cursor_attr = curses.A_REVERSE if app.monochrome else 0 @@ -1460,12 +1472,14 @@ def app_render_index_row(app: AppState, row: int, message: Message) -> None: subject_attr = app.colors["starred_subject"] if message.flags.starred else app.colors["default"] subject_attr = subject_attr | read_attr - app.screen.chgat(row, 1, len(date), app.colors["date"] | read_attr) - app.screen.chgat(row, len(date)+5, 10, app.colors["author"] | read_attr) - app.screen.chgat(row, len(date)+19, 4, app.colors["unread_comments"] | read_attr) - app.screen.chgat(row, 42, len(message.index_tree), app.colors["tree"]) - app.screen.chgat(row, 42 + len(message.index_tree), cols - 42 - len(message.index_tree), subject_attr) - + start = 1 + start = app_chgat(app, row, start, len(date), app.colors["date"] | read_attr) + start = app_chgat(app, row, start + 2 + COLUMN_SPACING, AUTHOR_SIZE, app.colors["author"] | read_attr) + start = app_chgat(app, row, start + 2 + COLUMN_SPACING, UNREAD_SIZE, app.colors["unread_comments"] | read_attr) + start = app_chgat(app, row, start + 1 + COLUMN_SPACING, len(message.index_tree), app.colors["tree"]) + app_chgat(app, row, start, cols - start, subject_attr) + + def app_render_index(app: AppState) -> None: height = app.layout.index_height