The community has a page, and the page is its invite
A fourth About page, `Join Discord`, sitting between the author and the ask: what is this, who made it, where is everyone, how do I help. Like its three siblings it is written markdown under assets/pages, reads nothing and transmits nothing, and keeps a CLI face — `meshterm discord`. It is short on purpose. The invite link, and the code for it in a `qr` fence, because on a PicoCalc there is no clipboard and the code is how a link leaves the screen at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
155c0b5b21
commit
6fc4e7afc9
6 files changed
+74
-15
No files matched your search
@@ -0,0 +1,9 @@
|
||||
## Join the Discord
|
||||
|
||||
Questions, ideas, bug reports, and mesh talk.
|
||||
|
||||
- *https://discord.gg/AZwe5Uvb3S*
|
||||
|
||||
```qr
|
||||
https://discord.gg/AZwe5Uvb3S
|
||||
```
|
||||
+26
-7
@@ -1,14 +1,15 @@
|
||||
"""The About MeshTerm tools: the three pages under the menu's last section.
|
||||
"""The About MeshTerm tools: the four pages under the menu's last section.
|
||||
|
||||
*About MeshTerm*, *About the author*, *Support MeshTerm* — in that order, which is
|
||||
the order a stranger asks the questions in: what is this, who made it, how do I help.
|
||||
*About MeshTerm*, *About the author*, *Join Discord*, *Support MeshTerm* — in that
|
||||
order, which is the order a stranger asks the questions in: what is this, who made it,
|
||||
where is everyone, how do I help.
|
||||
Each is a page rather than a feature: it reads nothing, transmits nothing, and needs no
|
||||
device, so it opens straight to its screen (see :mod:`meshterm.ui.about`) instead of
|
||||
prompting for anything first.
|
||||
|
||||
Each also keeps a CLI face — ``meshterm about``, ``meshterm about-author``,
|
||||
``meshterm support`` — printing the same page to the terminal, so the answers are
|
||||
reachable from a shell without launching the full-screen session.
|
||||
``meshterm discord``, ``meshterm support`` — printing the same page to the terminal, so
|
||||
the answers are reachable from a shell without launching the full-screen session.
|
||||
|
||||
The pages themselves are written in markdown under ``meshterm/assets/pages`` (see
|
||||
:mod:`meshterm.ui.about`); nothing here knows what any of them say.
|
||||
@@ -26,7 +27,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
class _AboutTool(Tool):
|
||||
"""Shared behaviour for the three About pages: open the screen, or print the page.
|
||||
"""Shared behaviour for the four About pages: open the screen, or print the page.
|
||||
|
||||
The pages differ only in their title, icon, and content, so everything else — the
|
||||
menu-only screen open, the printing CLI face, the run row — lives here once. A
|
||||
@@ -113,6 +114,24 @@ class AboutAuthorTool(_AboutTool):
|
||||
return about_author()
|
||||
|
||||
|
||||
@register
|
||||
class JoinDiscordTool(_AboutTool):
|
||||
"""The community server: one invite link, and a QR of it for a phone to read."""
|
||||
|
||||
name = "discord"
|
||||
title = "Join Discord"
|
||||
icon = "🔗"
|
||||
help = "The invite link to the MeshTerm community server"
|
||||
order = 25 # where everyone else is, once you know what this is and who made it
|
||||
|
||||
@staticmethod
|
||||
def page() -> "MarkdownDoc":
|
||||
"""The *Join Discord* page."""
|
||||
from ..ui.about import join_discord
|
||||
|
||||
return join_discord()
|
||||
|
||||
|
||||
@register
|
||||
class SupportProjectTool(_AboutTool):
|
||||
"""What keeps MeshTerm going, and the ways — paid and unpaid — to help it along."""
|
||||
@@ -121,7 +140,7 @@ class SupportProjectTool(_AboutTool):
|
||||
title = "Support MeshTerm"
|
||||
icon = "💰"
|
||||
help = "What keeps MeshTerm going, and how to help"
|
||||
order = 30 # the ask, and only once the first two have earned it
|
||||
order = 30 # the ask, and only once the pages before it have earned it
|
||||
|
||||
@staticmethod
|
||||
def page() -> "MarkdownDoc":
|
||||
|
||||
+11
-1
@@ -1,6 +1,6 @@
|
||||
"""The About MeshTerm pages: the app, the person who wrote it, and how to help it along.
|
||||
|
||||
Three read-only pages hung under the main menu's *About MeshTerm* section — the one
|
||||
Four read-only pages hung under the main menu's *About MeshTerm* section — the one
|
||||
section that names a subject rather than a doing, because "what is this thing, and who
|
||||
made it" is a question the other five can't answer. Each opens as a full screen, scrolls
|
||||
if the terminal is short, and is left with Esc; nothing here touches the radio or the
|
||||
@@ -72,6 +72,15 @@ def about_author() -> MarkdownDoc:
|
||||
return _page("author")
|
||||
|
||||
|
||||
def join_discord() -> MarkdownDoc:
|
||||
"""The *Join Discord* page: the invite link, and a QR of it to point a phone at.
|
||||
|
||||
A page rather than a popup because there is nothing to decide — it is a link, and on
|
||||
a headless console the QR *is* how the link gets off the screen.
|
||||
"""
|
||||
return _page("discord")
|
||||
|
||||
|
||||
def support_project() -> MarkdownDoc:
|
||||
"""The *Support MeshTerm* page: what keeps it going, and how to chip in.
|
||||
|
||||
@@ -166,6 +175,7 @@ __all__ = [
|
||||
"AboutPage",
|
||||
"about_author",
|
||||
"about_meshterm",
|
||||
"join_discord",
|
||||
"open_about_page",
|
||||
"support_project",
|
||||
]
|
||||
+15
-5
@@ -1,4 +1,4 @@
|
||||
"""About MeshTerm tests: the three pages, their placeholders, and their menu section.
|
||||
"""About MeshTerm tests: the four pages, their placeholders, and their menu section.
|
||||
|
||||
The pages carry no state and no controls, so what is worth pinning is what they *say*
|
||||
and how they are framed: the live package facts they must never drift from, the
|
||||
@@ -18,6 +18,7 @@ from meshterm.ui.about import (
|
||||
AboutPage,
|
||||
about_author,
|
||||
about_meshterm,
|
||||
join_discord,
|
||||
support_project,
|
||||
)
|
||||
|
||||
@@ -126,18 +127,27 @@ def test_the_licence_section_states_the_terms_and_the_credit_it_owes() -> None:
|
||||
assert owed in prose, owed
|
||||
|
||||
|
||||
def test_the_discord_page_carries_the_invite_link_and_a_code_to_scan_it_with() -> None:
|
||||
"""The link is the whole page, and on a console with no clipboard the QR is how it
|
||||
leaves the screen — so both have to survive the render."""
|
||||
text = _page(join_discord)
|
||||
|
||||
assert "https://discord.gg/AZwe5Uvb3S" in text
|
||||
assert "█" in text, "the scannable code didn't render"
|
||||
|
||||
|
||||
def test_pages_are_written_markdown_that_ships_with_the_package() -> None:
|
||||
"""Filling a page in is editing its ``.md`` file — no Python has to follow."""
|
||||
from meshterm.ui.about import _PAGES
|
||||
|
||||
for name in ("about", "author", "support"):
|
||||
for name in ("about", "author", "discord", "support"):
|
||||
source = (_PAGES / f"{name}.md").read_text(encoding="utf-8")
|
||||
assert source.lstrip().startswith("#"), f"{name}.md doesn't open with a heading"
|
||||
|
||||
|
||||
def test_no_package_placeholder_survives_into_the_rendered_page() -> None:
|
||||
"""The live facts are filled in as the page opens, so none of the braces reach a reader."""
|
||||
for builder in (about_meshterm, about_author, support_project):
|
||||
for builder in (about_meshterm, about_author, join_discord, support_project):
|
||||
text = _page(builder)
|
||||
assert "{" not in text and "}" not in text, text
|
||||
|
||||
@@ -194,7 +204,7 @@ def test_menu_rows_carry_the_icon_and_the_titles_do_not() -> None:
|
||||
"""Each page has a menu icon; none of it leaks into the title the screen draws."""
|
||||
from meshterm.tools import get_tool
|
||||
|
||||
for name in ("about", "about-author", "support"):
|
||||
for name in ("about", "about-author", "discord", "support"):
|
||||
tool = get_tool(name)
|
||||
assert tool is not None
|
||||
assert tool.icon, f"{name} has no menu icon"
|
||||
@@ -211,7 +221,7 @@ def test_every_page_icon_has_a_picocalc_glyph() -> None:
|
||||
|
||||
set_platform(PICOCALC)
|
||||
try:
|
||||
for name in ("about", "about-author", "support"):
|
||||
for name in ("about", "about-author", "discord", "support"):
|
||||
tool = get_tool(name)
|
||||
assert tool is not None
|
||||
compact = glyph(tool.icon)
|
||||
|
||||
+12
-1
@@ -54,7 +54,13 @@ from meshterm.services.message_paths import Arrival
|
||||
from meshterm.services.monitor_service import ACTIVITY_BUCKETS
|
||||
from meshterm.services.records import CATEGORY_BY_ID
|
||||
from meshterm.services.topology import MeshTopology, build_topology
|
||||
from meshterm.ui.about import AboutPage, about_author, about_meshterm, support_project
|
||||
from meshterm.ui.about import (
|
||||
AboutPage,
|
||||
about_author,
|
||||
about_meshterm,
|
||||
join_discord,
|
||||
support_project,
|
||||
)
|
||||
from meshterm.ui.chat import ChatScreen
|
||||
from meshterm.ui.contactlist import SORT_COLUMNS, SORT_OPENS_ASCENDING
|
||||
from meshterm.ui.contacts_screen import ContactsScreen
|
||||
@@ -493,6 +499,10 @@ def _support_project(cols: int, rows: int) -> Screen:
|
||||
return AboutPage("Support MeshTerm", support_project())
|
||||
|
||||
|
||||
def _join_discord(cols: int, rows: int) -> Screen:
|
||||
return AboutPage("Join Discord", join_discord())
|
||||
|
||||
|
||||
_ENTRIES: list[_Entry] = [
|
||||
_Entry("dashboard", _dashboard),
|
||||
_Entry("contacts", _contacts),
|
||||
@@ -515,6 +525,7 @@ _ENTRIES: list[_Entry] = [
|
||||
_Entry("tx_sweep", _tx_sweep),
|
||||
_Entry("about_meshterm", _about_meshterm),
|
||||
_Entry("about_author", _about_author),
|
||||
_Entry("join_discord", _join_discord),
|
||||
_Entry("support_project", _support_project),
|
||||
]
|
||||
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ def test_main_menu_sections_answer_the_menus_own_question() -> None:
|
||||
"Explore": ["map", "walk", "trace", "trace-path", "records"],
|
||||
"This node": ["info", "config", "device-actions", "advert"],
|
||||
"Other nodes": ["repeater-admin", "tx-optimize"],
|
||||
"About MeshTerm": ["about", "about-author", "support"],
|
||||
"About MeshTerm": ["about", "about-author", "discord", "support"],
|
||||
}
|
||||
# No section is so big it stops being a grouping (the old Mesh bucket held seven of
|
||||
# nineteen rows), and none is a bucket of one.
|
||||
|
||||
Reference in new issue
Block a user