mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:34:39 +08:00
tui: persistent @mention unread counter in title bar (UX-11)
The bell + brief yellow highlight on the chat line meant that if you
weren't looking at the screen the moment someone @-mentioned you, you
had no way to know.
Now the title bar carries a sticky chip:
tester · 在线 3 · NORMAL ★ 2 ? 帮助
- bright yellow "★ N" appears whenever client->unread_mentions > 0
- count is bumped atomically in notify_mentions() for each target
- cleared automatically when the user returns to attention:
* pressing 'i' in NORMAL to re-enter INSERT mode
* pressing 'G' in NORMAL to jump to the live tail
- never dropped by the narrow-terminal degradation (UX-6) unless
every other optional chip has already been shed — it's the highest
priority signal in the bar
Counter is _Atomic int so the cross-thread bump in notify_mentions
doesn't tear against the local thread's reads / resets.
This commit is contained in:
parent
70718482f3
commit
ddcecbea81
3 changed files with 25 additions and 1 deletions
|
|
@ -36,6 +36,7 @@ typedef struct client {
|
|||
time_t connect_time;
|
||||
time_t last_active;
|
||||
atomic_bool redraw_pending;
|
||||
_Atomic int unread_mentions; /* @-mentions received since last reset */
|
||||
bool mute_joins;
|
||||
pthread_t thread;
|
||||
atomic_bool connected;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ void notify_mentions(const char *content, const client_t *sender) {
|
|||
|
||||
for (int i = 0; i < target_count; i++) {
|
||||
client_send(targets[i], "\a", 1);
|
||||
targets[i]->unread_mentions++;
|
||||
targets[i]->redraw_pending = true;
|
||||
client_release(targets[i]);
|
||||
}
|
||||
|
|
@ -382,6 +383,7 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
|||
|
||||
if (key == 'i') {
|
||||
client->mode = MODE_INSERT;
|
||||
client->unread_mentions = 0;
|
||||
tui_render_screen(client);
|
||||
return true;
|
||||
} else if (key == ':') {
|
||||
|
|
@ -429,6 +431,7 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
|||
return true;
|
||||
} else if (key == 'G') {
|
||||
client->scroll_pos = nm_max_scroll;
|
||||
client->unread_mentions = 0;
|
||||
tui_render_screen(client);
|
||||
return true;
|
||||
} else if (key == '?') {
|
||||
|
|
|
|||
22
src/tui.c
22
src/tui.c
|
|
@ -360,10 +360,22 @@ void tui_render_screen(client_t *client) {
|
|||
int hint_width = utf8_string_width(hint);
|
||||
int mute_width = client->mute_joins ? 6 : 0; /* " 静音" = 2 + 4 */
|
||||
|
||||
/* Unread @-mentions chip — high-priority, gets a bright yellow star.
|
||||
* Sits between mode and hint when present, and survives degradation
|
||||
* longer than the hint / mute / mode chips. */
|
||||
int unread_count = client->unread_mentions;
|
||||
char unread_buf[32] = "";
|
||||
int unread_width = 0;
|
||||
if (unread_count > 0) {
|
||||
snprintf(unread_buf, sizeof(unread_buf), "★ %d", unread_count);
|
||||
unread_width = utf8_string_width(unread_buf) + 2; /* leading " · " minus initial space accounted later */
|
||||
}
|
||||
|
||||
/* Decide what fits. Reserve at least 1 col of gap between left and
|
||||
* right halves so they never visually touch. */
|
||||
int show_hint = 1;
|
||||
int show_mute = client->mute_joins ? 1 : 0;
|
||||
int show_unread = unread_count > 0 ? 1 : 0;
|
||||
int show_chips = chip_count;
|
||||
|
||||
while (show_chips > 1) {
|
||||
|
|
@ -373,14 +385,17 @@ void tui_render_screen(client_t *client) {
|
|||
left_w += utf8_string_width(chips[i].value);
|
||||
}
|
||||
if (show_mute) left_w += mute_width;
|
||||
if (show_unread) left_w += unread_width + 1; /* + " " separator */
|
||||
int right_w = (show_hint ? hint_width + 1 /*trailing space*/ : 0);
|
||||
int needed = left_w + 1 /*min gap*/ + right_w;
|
||||
if (needed <= render_width) break;
|
||||
|
||||
/* Drop in priority order: hint → mute → mode chip → online count. */
|
||||
/* Drop in priority order: hint → mute → mode chip → online count.
|
||||
* Unread is sticky — only dropped if everything else already is. */
|
||||
if (show_hint) { show_hint = 0; continue; }
|
||||
if (show_mute) { show_mute = 0; continue; }
|
||||
if (show_chips > 1) { show_chips--; continue; }
|
||||
if (show_unread) { show_unread = 0; continue; }
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -401,6 +416,11 @@ void tui_render_screen(client_t *client) {
|
|||
buffer_appendf(left, sizeof(left), &lpos, " \033[2;37m静音\033[0m");
|
||||
left_width += mute_width;
|
||||
}
|
||||
if (show_unread) {
|
||||
buffer_appendf(left, sizeof(left), &lpos,
|
||||
" \033[1;33m%s\033[0m", unread_buf);
|
||||
left_width += unread_width + 1;
|
||||
}
|
||||
|
||||
int gap = render_width - left_width - (show_hint ? hint_width + 2 : 1);
|
||||
if (gap < 1) gap = 1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue