mirror of
https://github.com/markqvist/NomadNet.git
synced 2026-10-25 18:40:13 -04:00
Added markdown rendering and compose support to messages
This commit is contained in:
parent
4114df70d1
commit
f640f074d8
8 changed files with 109 additions and 56 deletions
|
|
@ -417,6 +417,7 @@ class ConversationMessage:
|
|||
self.timestamp = self.lxm.timestamp
|
||||
self.sort_timestamp = os.path.getmtime(self.file_path)
|
||||
|
||||
|
||||
if self.lxm.state > LXMF.LXMessage.GENERATING and self.lxm.state < LXMF.LXMessage.SENT:
|
||||
found = False
|
||||
|
||||
|
|
@ -597,6 +598,18 @@ class ConversationMessage:
|
|||
|
||||
return {}
|
||||
|
||||
def content_renderer(self):
|
||||
if not self.loaded: self.load()
|
||||
RNS.log(self.lxm)
|
||||
if self.lxm and hasattr(self.lxm, "get_fields"):
|
||||
fields = self.lxm.get_fields()
|
||||
RNS.log(f"has fields: {fields}")
|
||||
if fields and isinstance(fields, dict):
|
||||
if LXMF.FIELD_RENDERER in fields:
|
||||
return fields[LXMF.FIELD_RENDERER]
|
||||
|
||||
return None
|
||||
|
||||
def has_attachments(self):
|
||||
if self._cached_has_attachments is not None:
|
||||
return self._cached_has_attachments
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ class NomadNetworkApp:
|
|||
self.try_propagation_on_fail = True
|
||||
self.disable_propagation = True
|
||||
self.notify_on_new_message = True
|
||||
self.compose_markdown = True
|
||||
|
||||
self.lxmf_max_propagation_size = None
|
||||
self.lxmf_max_sync_size = None
|
||||
|
|
@ -836,6 +837,10 @@ class NomadNetworkApp:
|
|||
value = self.config["client"].as_bool(option)
|
||||
self.notify_on_new_message = value
|
||||
|
||||
if option == "compose_in_markdown":
|
||||
value = self.config["client"].as_bool(option)
|
||||
self.compose_markdown = value
|
||||
|
||||
if option == "user_interface":
|
||||
value = value.lower()
|
||||
if value == "none":
|
||||
|
|
@ -1146,7 +1151,6 @@ downloads_path = ~/Downloads
|
|||
# Where to save received attachments. If not set,
|
||||
# attachments will be saved to the downloads path.
|
||||
# attachment_save_path = ~/Downloads
|
||||
|
||||
notify_on_new_message = yes
|
||||
|
||||
# By default, the peer is announced at startup
|
||||
|
|
@ -1210,6 +1214,10 @@ max_accepted_size = 500
|
|||
# been received, for every destination.
|
||||
compact_announce_stream = yes
|
||||
|
||||
# You can choose whether or not to compose
|
||||
# messages in markdown format.
|
||||
compose_in_markdown = yes
|
||||
|
||||
[textui]
|
||||
|
||||
# Amount of time to show intro screen
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import os
|
|||
import platform
|
||||
|
||||
import nomadnet
|
||||
from nomadnet.ui import THEME_DARK, THEME_LIGHT
|
||||
from nomadnet.ui.textui import *
|
||||
from nomadnet import NomadNetworkApp
|
||||
|
||||
|
|
@ -13,8 +14,6 @@ COLORMODE_16 = 16
|
|||
COLORMODE_88 = 88
|
||||
COLORMODE_256 = 256
|
||||
COLORMODE_TRUE = 2**24
|
||||
THEME_DARK = 0x01
|
||||
THEME_LIGHT = 0x02
|
||||
|
||||
THEMES = {
|
||||
THEME_DARK: {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ pyc_modules = glob.glob(os.path.dirname(__file__)+"/*.pyc")
|
|||
modules = py_modules+pyc_modules
|
||||
__all__ = list(set([os.path.basename(f).replace(".pyc", "").replace(".py", "") for f in modules if not (f.endswith("__init__.py") or f.endswith("__init__.pyc"))]))
|
||||
|
||||
THEME_DARK = 0x01
|
||||
THEME_LIGHT = 0x02
|
||||
|
||||
UI_NONE = 0x00
|
||||
UI_MENU = 0x01
|
||||
|
|
|
|||
|
|
@ -13,10 +13,11 @@ 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
|
||||
from nomadnet.util import strip_modifiers, strip_micron, strip_escaped_micron, unescape_micron, strip_non_formatting_tags
|
||||
|
||||
|
||||
theme_dark = { "ts": "888",
|
||||
theme_dark = { "text": "ddd",
|
||||
"ts": "888",
|
||||
"nick_self": "6c5",
|
||||
"nick_peer": "3cd",
|
||||
"notice": "fd3",
|
||||
|
|
@ -25,7 +26,8 @@ theme_dark = { "ts": "888",
|
|||
"mention": "fb4",
|
||||
"link": "79d", }
|
||||
|
||||
theme_light = { "ts": "888",
|
||||
theme_light = { "text": "111",
|
||||
"ts": "888",
|
||||
"nick_self": "3a0",
|
||||
"nick_peer": "077",
|
||||
"notice": "a70",
|
||||
|
|
@ -899,47 +901,6 @@ 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):
|
||||
|
|
@ -1023,22 +984,22 @@ def _message_widget(app, hub, m, link_delegate=None):
|
|||
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}")
|
||||
prefix_rendered = _render_body(f"{prefix_micron}", fg=t["text"])
|
||||
body_rendered = _render_body(f"{nick_micron}{message_body}", fg=t["text"])
|
||||
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)
|
||||
rendered = _render_body(f"{prefix_micron}{nick_micron}{message_body}", link_delegate=ld, fg=t["text"])
|
||||
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
|
||||
|
||||
def _render_body(markup, link_delegate=None):
|
||||
try: return markup_to_attrmaps(strip_modifiers(markup), url_delegate=link_delegate, link_class=_ChatLinkableText)
|
||||
def _render_body(markup, link_delegate=None, fg="bbb"):
|
||||
try: return markup_to_attrmaps(strip_modifiers(markup), url_delegate=link_delegate, link_class=_ChatLinkableText, fg_color=fg)
|
||||
except Exception as e:
|
||||
RNS.trace_exception(e)
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@ from nomadnet.Conversation import ConversationMessage
|
|||
from nomadnet.util import strip_modifiers
|
||||
from nomadnet.util import sanitize_name
|
||||
|
||||
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, strip_micron, strip_escaped_micron, unescape_micron, strip_non_formatting_tags
|
||||
from nomadnet.ui import THEME_DARK, THEME_LIGHT
|
||||
|
||||
def relative_time(timestamp):
|
||||
now = time.time()
|
||||
|
|
@ -1302,7 +1307,7 @@ class ConversationWidget(urwid.WidgetWrap):
|
|||
if not message_hash in added_hashes:
|
||||
added_hashes.append(message_hash)
|
||||
was_loaded = message.loaded
|
||||
message_widget = LXMessageWidget(message)
|
||||
message_widget = LXMessageWidget(message, theme=self.app.config["textui"]["theme"])
|
||||
self.message_widgets.append(message_widget)
|
||||
if not was_loaded and message.loaded:
|
||||
needs_index.append(message)
|
||||
|
|
@ -1467,6 +1472,10 @@ class ConversationWidget(urwid.WidgetWrap):
|
|||
if file_attachments:
|
||||
fields = {LXMF.FIELD_FILE_ATTACHMENTS: file_attachments}
|
||||
|
||||
if self.app.compose_markdown:
|
||||
if not fields: fields = {}
|
||||
fields[LXMF.FIELD_RENDERER] = LXMF.RENDERER_MARKDOWN
|
||||
|
||||
if self.conversation.send(content, title, fields=fields):
|
||||
self.clear_editor()
|
||||
|
||||
|
|
@ -1609,7 +1618,9 @@ class ConversationWidget(urwid.WidgetWrap):
|
|||
|
||||
|
||||
class LXMessageWidget(urwid.WidgetWrap):
|
||||
def __init__(self, message):
|
||||
mdc = MarkdownToMicron(max_width=80, syntax_highlighter=SyntaxHighlighter(), url_scope=None)
|
||||
|
||||
def __init__(self, message, theme=THEME_DARK):
|
||||
app = nomadnet.NomadNetworkApp.get_shared_instance()
|
||||
g = app.ui.glyphs
|
||||
self.timestamp = message.get_timestamp()
|
||||
|
|
@ -1623,6 +1634,7 @@ class LXMessageWidget(urwid.WidgetWrap):
|
|||
msg_method = message._cached_method
|
||||
time_format = app.time_format
|
||||
message_time = datetime.fromtimestamp(self.timestamp)
|
||||
renderer = message.content_renderer()
|
||||
encryption_string = ""
|
||||
if message.get_transport_encrypted():
|
||||
encryption_string = " "+g["encrypted"]
|
||||
|
|
@ -1685,7 +1697,16 @@ class LXMessageWidget(urwid.WidgetWrap):
|
|||
|
||||
content_text = message.get_content()
|
||||
content_lines = content_text.split("\n")
|
||||
indented = "\n".join(" "+line for line in content_lines)
|
||||
markdown = renderer == LXMF.RENDERER_MARKDOWN
|
||||
|
||||
default_fg = "bbb" if theme == THEME_DARK else "444"
|
||||
if markdown:
|
||||
formatted = self.mdc.format_block(content_text)
|
||||
message_body = strip_non_formatting_tags(formatted)
|
||||
rendered = markup_to_attrmaps(strip_modifiers(message_body), url_delegate=None, fg_color=default_fg, bg_color=None)
|
||||
content_pile = urwid.Padding(urwid.Pile(rendered), left=2, right=2)
|
||||
|
||||
else: indented = "\n".join(" "+line for line in content_lines)
|
||||
|
||||
pile_widgets = [title]
|
||||
|
||||
|
|
@ -1711,7 +1732,8 @@ class LXMessageWidget(urwid.WidgetWrap):
|
|||
pile_widgets.append(self.progress_attr)
|
||||
self._start_progress_poll()
|
||||
|
||||
pile_widgets.append(urwid.Text(indented))
|
||||
if markdown: pile_widgets.append(content_pile)
|
||||
else: pile_widgets.append(urwid.Text(indented))
|
||||
|
||||
if has_attachments and cached_names:
|
||||
att_file_idx = 0
|
||||
|
|
|
|||
|
|
@ -771,6 +771,12 @@ The maximum accepted unpacked size for messages received directly from other pee
|
|||
With this option enabled, Nomad Network will only display one entry in the announce stream per destination. Older announces are culled when a new one arrives.
|
||||
<
|
||||
|
||||
>>>
|
||||
`!compose_in_markdown = yes`!
|
||||
>>>>
|
||||
Configures whether sent messages are composed in markdown format.
|
||||
<
|
||||
|
||||
>> RRC Section
|
||||
|
||||
This section hold configuration directives related to the RRC client behaviour. It is delimited by the `![rrc]`! header in the configuration file. Available directives, along with their default values, are as follows:
|
||||
|
|
|
|||
|
|
@ -141,3 +141,45 @@ def sanitize_name(name):
|
|||
name = name.strip()
|
||||
|
||||
return name
|
||||
|
||||
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
|
||||
Loading…
Add table
Add a link
Reference in a new issue