Every main-menu item leads with its own emoji
Tools gain an icon attribute — menu-only, so result windows and the CLI keep the plain title — and the menu loop composes it into the aligned name lane. Single-codepoint, emoji-presentation glyphs only, so the two-cell width holds across terminals: 💬 Chat, 📻 Channels, 🌍 Map, 👥 Nodes, 📡 Send advert, 🔧 configuration, 🔨 actions, 📋 info, 🎯/👣 the traces, 🎧 Monitor, 📶 TX optimize, 🚪 Quit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5027d05943
commit
87d2d10dff
13 files changed
+25
-3
No files matched your search
@@ -27,6 +27,7 @@ class SendAdvertTool(Tool):
|
||||
|
||||
name = "advert"
|
||||
title = "Send advert"
|
||||
icon = "📡"
|
||||
help = "Announce this node to the mesh — zero-hop, flood, or share as QR"
|
||||
category = "Device"
|
||||
order = 10 # right after Device actions, before Device info
|
||||
|
||||
@@ -42,6 +42,10 @@ class Tool(ABC):
|
||||
window. Capitalized, and kept identical to the screen the item opens so the
|
||||
menu never promises one name and delivers another. Falls back to ``name``.
|
||||
help: One-line description shown in the menu and ``--help`` (no trailing period).
|
||||
icon: Emoji drawn before the title in the interactive menu (and only there — the
|
||||
result window keeps the plain title). Single-codepoint, emoji-presentation
|
||||
glyphs only, so the two-cell width holds across terminals (see
|
||||
:mod:`meshterm.ui.tui.emoji_width`).
|
||||
category: Grouping label used to organize the interactive menu.
|
||||
order: Sort key within a category (lower sorts first).
|
||||
menu_visible: Whether the tool appears in the interactive menu. Set ``False`` for
|
||||
@@ -52,6 +56,7 @@ class Tool(ABC):
|
||||
name: str = ""
|
||||
title: str = ""
|
||||
help: str = ""
|
||||
icon: str = ""
|
||||
category: str = "General"
|
||||
order: int = 100
|
||||
menu_visible: bool = True
|
||||
|
||||
@@ -36,6 +36,7 @@ class ChannelsTool(Tool):
|
||||
|
||||
name = "channels"
|
||||
title = "Channels"
|
||||
icon = "📻"
|
||||
help = "Create, join, and share mesh channels (with QR codes)"
|
||||
category = "Messaging"
|
||||
order = 20
|
||||
|
||||
@@ -44,6 +44,7 @@ class ChatTool(Tool):
|
||||
|
||||
name = "chat"
|
||||
title = "Chat"
|
||||
icon = "💬"
|
||||
help = "Channel and direct messaging — live chat, with stored history"
|
||||
category = "Messaging"
|
||||
order = 10
|
||||
|
||||
@@ -36,6 +36,7 @@ class ConfigTool(Tool):
|
||||
|
||||
name = "config"
|
||||
title = "Device configuration"
|
||||
icon = "🔧"
|
||||
help = "View and change device settings — identity, radio, tuning, …"
|
||||
category = "Device"
|
||||
order = 8
|
||||
|
||||
@@ -30,6 +30,7 @@ class DeviceActionsTool(Tool):
|
||||
|
||||
name = "device-actions"
|
||||
title = "Device actions"
|
||||
icon = "🔨"
|
||||
help = "Act on the device itself — advert, backup, identity key, reboot, reset"
|
||||
category = "Device"
|
||||
order = 9
|
||||
|
||||
@@ -26,6 +26,7 @@ class InfoTool(Tool):
|
||||
|
||||
name = "info"
|
||||
title = "Device info"
|
||||
icon = "📋"
|
||||
help = "Show device status, identity, and radio config"
|
||||
category = "Device"
|
||||
order = 11 # after Send advert (10), which slots in behind Device actions
|
||||
|
||||
@@ -35,6 +35,7 @@ class MapTool(Tool):
|
||||
|
||||
name = "map"
|
||||
title = "Map"
|
||||
icon = "🌍"
|
||||
help = "Show mesh nodes on a street map (pannable; repeaters highlighted)"
|
||||
category = "Messaging"
|
||||
order = 30
|
||||
|
||||
@@ -36,6 +36,7 @@ class MonitorTool(Tool):
|
||||
|
||||
name = "monitor"
|
||||
title = "Monitor"
|
||||
icon = "🎧"
|
||||
help = "Capture overheard packets live for a while and summarize them"
|
||||
category = "Diagnostics"
|
||||
order = 20
|
||||
|
||||
@@ -16,6 +16,7 @@ class NodesTool(Tool):
|
||||
|
||||
name = "nodes"
|
||||
title = "Nodes"
|
||||
icon = "👥"
|
||||
help = "List this node and known contacts (last heard, packets, type, key)"
|
||||
category = "Device"
|
||||
order = 12
|
||||
|
||||
@@ -48,6 +48,7 @@ class TraceTool(Tool):
|
||||
|
||||
name = "trace"
|
||||
title = "Trace target"
|
||||
icon = "🎯"
|
||||
help = "Trace a target over a mirrored route and watch per-hop SNR"
|
||||
category = "Diagnostics"
|
||||
order = 10
|
||||
@@ -222,6 +223,7 @@ class TracePathTool(Tool):
|
||||
|
||||
name = "trace-path"
|
||||
title = "Trace path"
|
||||
icon = "👣"
|
||||
help = "Walk a hand-composed route — out and back your way"
|
||||
category = "Diagnostics"
|
||||
order = 11
|
||||
|
||||
@@ -37,6 +37,7 @@ class TxOptimizeTool(Tool):
|
||||
|
||||
name = "tx-optimize"
|
||||
title = "TX optimize"
|
||||
icon = "📶"
|
||||
help = "Tune a remote node's TX power for the best signal at a target"
|
||||
category = "Diagnostics"
|
||||
order = 15 # right after Trace, its measurement sibling
|
||||
|
||||
+8
-3
@@ -322,20 +322,25 @@ async def _menu_loop(ctx: AppContext, session: TuiSession) -> None:
|
||||
# Two aligned columns — tool name, then its muted description — with no header
|
||||
# line: these are commands, not tabular data, so the alignment alone carries it.
|
||||
tools = [tool for tool in all_tools() if tool.menu_visible]
|
||||
name_w = max((cell_len(tool.title or tool.name) for tool in tools), default=0)
|
||||
|
||||
def _label(tool) -> str: # noqa: ANN001 - registry Tool; typed at the source
|
||||
title = tool.title or tool.name
|
||||
return f"{tool.icon} {title}" if tool.icon else title
|
||||
|
||||
name_w = max((cell_len(_label(tool)) for tool in tools), default=0)
|
||||
items: list = []
|
||||
current_category: str | None = None
|
||||
for tool in tools:
|
||||
if tool.category != current_category:
|
||||
current_category = tool.category
|
||||
items.append(Separator(f"── {current_category} ──", style="accent"))
|
||||
label = tool.title or tool.name
|
||||
label = _label(tool)
|
||||
row = Text(label)
|
||||
row.append(" " * (name_w - cell_len(label) + 2))
|
||||
row.append(tool.help, style="muted")
|
||||
items.append(Choice(title=row, value=tool.name))
|
||||
items.append(Separator(" "))
|
||||
items.append(Choice(title="Quit", value="__quit__"))
|
||||
items.append(Choice(title="🚪 Quit", value="__quit__"))
|
||||
|
||||
# Drive the menu list ourselves (rather than via session.select) so it stays on the
|
||||
# stack while the quit dialog floats over it: the confirm is drawn as a centered box
|
||||
|
||||
Reference in new issue
Block a user