From 4114df70d1cd02c27ca0ca7a7cd6e74370ee362f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 19 May 2026 21:23:02 +0200 Subject: [PATCH] Added markdown rendering support, config options for enabling/disabling markdown and micron rendering, justify_msgs and space_msgs layout options. --- nomadnet/NomadNetworkApp.py | 30 ++++++++++++++ nomadnet/ui/textui/Channels.py | 73 +++++++++++++++++++++++++++++++--- nomadnet/ui/textui/Guide.py | 19 +++++++++ 3 files changed, 117 insertions(+), 5 deletions(-) diff --git a/nomadnet/NomadNetworkApp.py b/nomadnet/NomadNetworkApp.py index 4e14b50..f3afd73 100644 --- a/nomadnet/NomadNetworkApp.py +++ b/nomadnet/NomadNetworkApp.py @@ -155,6 +155,10 @@ class NomadNetworkApp: self.rrc_history_per_room_cap = 500 self.rrc_filter_loaded_history = True self.rrc_ephemeral_notices = 600 + self.rrc_ui_justify_msgs = False + self.rrc_ui_space_msgs = False + self.rrc_ui_render_markdown = True + self.rrc_ui_render_micron = True if not os.path.isdir(self.storagepath): os.makedirs(self.storagepath) @@ -925,6 +929,26 @@ class NomadNetworkApp: except Exception: value = 0 self.rrc_ephemeral_notices = value*60 + if option == "justify_msgs": + try: value = self.config["rrc"].as_bool(option) + except Exception: value = False + self.rrc_ui_justify_msgs = value + + if option == "space_msgs": + try: value = self.config["rrc"].as_bool(option) + except Exception: value = False + self.rrc_ui_space_msgs = value + + if option == "render_markdown": + try: value = self.config["rrc"].as_bool(option) + except Exception: value = True + self.rrc_ui_render_markdown = value + + if option == "render_micron": + try: value = self.config["rrc"].as_bool(option) + except Exception: value = True + self.rrc_ui_render_micron = value + if "node" in self.config: if not "enable_node" in self.config["node"]: self.enable_node = False @@ -1261,6 +1285,12 @@ filter_loaded_history = yes # to disable and keep forever. ephemeral_notices = 10 +# Other display and formatting options: +render_markdown = yes +render_micron = yes +justify_msgs = no +space_msgs = no + [node] # Whether to enable node hosting diff --git a/nomadnet/ui/textui/Channels.py b/nomadnet/ui/textui/Channels.py index 00162f9..ea119d8 100644 --- a/nomadnet/ui/textui/Channels.py +++ b/nomadnet/ui/textui/Channels.py @@ -10,6 +10,8 @@ import nomadnet from nomadnet.RRC import RRCHub from nomadnet.vendor.additional_urwid_widgets import IndicativeListBox from nomadnet.ui.textui.MicronParser import LinkableText, LinkSpec +from RNS.Utilities.rngit.util import MarkdownToMicron +from RNS.Utilities.rngit.highlight import SyntaxHighlighter from .MicronParser import markup_to_attrmaps from nomadnet.util import strip_modifiers @@ -897,6 +899,49 @@ class _ChatLinkDelegate: except Exception as e: RNS.log("Could not open page link: "+str(e), RNS.LOG_ERROR) +def strip_micron(text): + text = re.sub(r'`[FB][0-9a-fA-F]{3}', '', text) + text = re.sub(r'`[FB]T[0-9a-fA-F]{6}', '', text) + text = re.sub(r'`[!*_=]', '', text) + text = re.sub(r'`f`b', '', text) + text = re.sub(r'`f', '', text) + text = re.sub(r'`b', '', text) + text = re.sub(r'`<', '', text) + text = re.sub(r'`>', '', text) + text = re.sub(r'`{', '', text) + return text + +def strip_escaped_micron(text): + text = re.sub(r'¦[FB][0-9a-fA-F]{3}', '', text) + text = re.sub(r'¦[FB]T[0-9a-fA-F]{6}', '', text) + text = re.sub(r'¦[!*_=]', '', text) + text = re.sub(r'¦f`b', '', text) + text = re.sub(r'¦f', '', text) + text = re.sub(r'¦b', '', text) + text = re.sub(r'¦<', '', text) + text = re.sub(r'¦>', '', text) + text = re.sub(r'¦{', '', text) + return text + +def unescape_micron(text): + text = re.sub(r'¦([FB][0-9a-fA-F]{3})', r'`\1', text) + text = re.sub(r'¦([FB]T[0-9a-fA-F]{6})', r'`\1', text) + text = re.sub(r'¦([!*_=])', r'`\1', text) + text = re.sub(r'¦(f`b)', r'`\1', text) + text = re.sub(r'¦(f)', r'`\1', text) + text = re.sub(r'¦(b)', r'`\1', text) + text = re.sub(r'¦(<)', r'`\1', text) + text = re.sub(r'¦(>)', r'`\1', text) + text = re.sub(r'¦({)', r'`\1', text) + return text + +def strip_non_formatting_tags(text): + text = re.sub(r'`<', '', text) + text = re.sub(r'`>', '', text) + text = re.sub(r'`{', '', text) + return text + +mdc = MarkdownToMicron(max_width=80, syntax_highlighter=SyntaxHighlighter(), url_scope=None) def _message_widget(app, hub, m, link_delegate=None): t = theme_dark if app.config["textui"]["theme"] == nomadnet.ui.TextUI.THEME_DARK else theme_light g = app.ui.glyphs @@ -960,16 +1005,34 @@ def _message_widget(app, hub, m, link_delegate=None): label = mb.split("`")[0] url = f"{kind}://{mb}" link_mu = f"`_`F{t['link']}`[{label}`{url}]`f`_" + link_md = f"[{label}]({url})" message_body += link_mu - else: message_body += mb + else: + if app.rrc_ui_render_micron: + mbo = mdc.format_block(mb) if app.rrc_ui_render_markdown else mb + mbo = unescape_micron(mbo) + message_body += strip_non_formatting_tags(mbo) + + else: + mbo = mdc.format_block(strip_escaped_micron(mb)) if app.rrc_ui_render_markdown else strip_escaped_micron(mb) + message_body += strip_non_formatting_tags(mbo) nick_attr = f"`F{t['nick_self']}" if own else f"`F{t['nick_peer']}" irc_ts = f"`F{t['ts']}" - prefix_micron = f"{irc_ts}{_ts_prefix_raw(m.ts)}`f {nick_attr}<{sender}>`f " - - rendered = _render_body(f"{prefix_micron}{message_body}", link_delegate=ld) - final_widget = urwid.Padding(urwid.Pile(rendered), left=1) + prefix_micron = f"{irc_ts}{_ts_prefix_raw(m.ts)}" + nick_micron = f"`f{nick_attr}<{sender}>`f " + if app.rrc_ui_justify_msgs: + prefix_rendered = _render_body(f"{prefix_micron}") + body_rendered = _render_body(f"{nick_micron}{message_body}") + if app.rrc_ui_space_msgs: body_rendered.append(urwid.Text("")) + columns = urwid.Columns([(urwid.PACK, urwid.Pile(prefix_rendered)), urwid.Pile(body_rendered)], dividechars=1) + final_widget = urwid.Padding(columns, left=1) + + else: + rendered = _render_body(f"{prefix_micron}{nick_micron}{message_body}", link_delegate=ld) + if app.rrc_ui_space_msgs: rendered.append(urwid.Text("")) + final_widget = urwid.Padding(urwid.Pile(rendered), left=1) final_widget.msg = m return final_widget diff --git a/nomadnet/ui/textui/Guide.py b/nomadnet/ui/textui/Guide.py index 66def19..840dcc8 100644 --- a/nomadnet/ui/textui/Guide.py +++ b/nomadnet/ui/textui/Guide.py @@ -793,6 +793,25 @@ Whether to filter notices and system message when initially loading room history If enabled, notices and system messages will disappear from the history after a while. 0 disables, otherwise sets the timeout in minutes. < +>>> +`!render_markdown = yes`! +>>>> +Whether or not to render markdown formatting in messages. +< + +>>> +`!render_micron = yes`! +>>>> +Whether or not to render micron formatting in messages. When using micron in messages, use the ¦ character in place of backticks. +< + +>>> +`!justify_msgs = no`! +`!space_msgs = no`! +>>>> +Rendering layout options for messages. +< + >> Text UI Section This section hold configuration directives related to the look and feel of the text-based user interface of the program. It is delimited by the `![textui]`! header in the configuration file. Available directives, along with their default values, are as follows: