Three interrelated improvements to the chat/channel experience: Outgoing byte budget & unified transcript (ui/chat.py, ui/tui/prompt.py): - Cap outgoing messages at MeshCore's UTF-8 limits (150B direct, 130B channel) and block over-limit sends, keeping the buffer intact to trim. - Show a live `used/limit` gauge in the compose bar whose used count steps green→yellow→orange→red by bytes remaining, with truer yellow/orange hues. - Color the overflow characters red in the compose line (multibyte-safe). - Render direct chats with the same grouped, per-sender layout as channels (headers, day dividers) instead of one line per message. Direct-message delivery retry (services/chat_service.py, persistence, core/models.py, tools/chat.py, ui/tui/session.py): - Track outbound acked state (None awaiting / True / False) with a persisted row_id, add resend_direct + update_chat_ack, and wire Ctrl-R to retry the latest unacknowledged message in place with ⏳/✅/❌ delivery glyphs. Channel slot capacity discovery (core/channels.py, core/connection.py, ui/channels.py, tools): - Discover the firmware's real channel-slot count via a bounded read-only probe (Device.channel_capacity) instead of assuming 8; show used/total in the manager and refresh the chat slot→identity cache after any mutation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
"""Shared Rich theme and console factory for a consistent, modern look."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rich.console import Console
|
|
from rich.theme import Theme
|
|
|
|
MESH_THEME = Theme(
|
|
{
|
|
"brand": "bold #5eead4",
|
|
"accent": "bold #818cf8",
|
|
# Our own messages in a chat: pure white, deliberately outside the per-sender hue
|
|
# palette (see meshtools.ui.chat._SENDER_COLORS) so "you" is always easy to spot.
|
|
"you": "bold #ffffff",
|
|
"ok": "bold #4ade80",
|
|
"warn": "bold #fbbf24",
|
|
"err": "bold #f87171",
|
|
"muted": "#94a3b8",
|
|
"snr.good": "bold #4ade80",
|
|
"snr.ok": "bold #fbbf24",
|
|
"snr.bad": "bold #f87171",
|
|
}
|
|
)
|
|
|
|
|
|
def make_console() -> Console:
|
|
"""Create the application's themed Rich console.
|
|
|
|
On legacy Windows consoles the standard streams default to ``cp1252``, which cannot
|
|
encode the box-drawing and marker glyphs (``◆ ● ★``) the UI uses; this reconfigures
|
|
them to UTF-8 where the runtime supports it so output never raises ``UnicodeEncodeError``.
|
|
|
|
Returns:
|
|
A :class:`rich.console.Console` configured with the MeshTools theme.
|
|
"""
|
|
import sys
|
|
|
|
for stream in (sys.stdout, sys.stderr):
|
|
reconfigure = getattr(stream, "reconfigure", None)
|
|
if reconfigure is not None:
|
|
try:
|
|
reconfigure(encoding="utf-8")
|
|
except (ValueError, OSError): # pragma: no cover - stream not reconfigurable
|
|
pass
|
|
return Console(theme=MESH_THEME)
|
|
|
|
|
|
def snr_style(snr: float | None) -> str:
|
|
"""Return a theme style name describing an SNR value's quality.
|
|
|
|
Args:
|
|
snr: An SNR reading in dB, or ``None``.
|
|
|
|
Returns:
|
|
``"snr.good"``, ``"snr.ok"``, ``"snr.bad"``, or ``"muted"`` for ``None``.
|
|
"""
|
|
if snr is None:
|
|
return "muted"
|
|
if snr >= 5:
|
|
return "snr.good"
|
|
if snr >= -5:
|
|
return "snr.ok"
|
|
return "snr.bad"
|