diff --git a/meshchatx/src/frontend/components/messages/ConversationViewer.vue b/meshchatx/src/frontend/components/messages/ConversationViewer.vue index 6911874..2dd13bf 100644 --- a/meshchatx/src/frontend/components/messages/ConversationViewer.vue +++ b/meshchatx/src/frontend/components/messages/ConversationViewer.vue @@ -2061,6 +2061,18 @@ export default { }); } } + + const lxmfLink = event.target.closest(".lxmf-link"); + if (lxmfLink) { + event.preventDefault(); + const address = lxmfLink.getAttribute("data-lxmf-address"); + if (address) { + this.$router.push({ + name: "messages", + params: { destinationHash: address }, + }); + } + } }, async updatePropagationNodeStatus() { try { diff --git a/meshchatx/src/frontend/components/rncp/RNCPPage.vue b/meshchatx/src/frontend/components/rncp/RNCPPage.vue index 43532fe..0c3d7b4 100644 --- a/meshchatx/src/frontend/components/rncp/RNCPPage.vue +++ b/meshchatx/src/frontend/components/rncp/RNCPPage.vue @@ -541,6 +541,18 @@ export default { }); } } + + const lxmfLink = event.target.closest(".lxmf-link"); + if (lxmfLink) { + event.preventDefault(); + const address = lxmfLink.getAttribute("data-lxmf-address"); + if (address) { + this.$router.push({ + name: "messages", + params: { destinationHash: address }, + }); + } + } }, }, }; diff --git a/meshchatx/src/frontend/components/tools/RNPathTracePage.vue b/meshchatx/src/frontend/components/tools/RNPathTracePage.vue index 78fdf80..46c4fcd 100644 --- a/meshchatx/src/frontend/components/tools/RNPathTracePage.vue +++ b/meshchatx/src/frontend/components/tools/RNPathTracePage.vue @@ -69,7 +69,7 @@ -
+
diff --git a/meshchatx/src/frontend/js/LinkUtils.js b/meshchatx/src/frontend/js/LinkUtils.js index c19a80c..4d939e8 100644 --- a/meshchatx/src/frontend/js/LinkUtils.js +++ b/meshchatx/src/frontend/js/LinkUtils.js @@ -1,19 +1,35 @@ export default class LinkUtils { /** - * Detects and wraps NomadNet links in HTML. - * Supports nomadnet://:/path and :/path + * Detects and wraps Reticulum (NomadNet and LXMF) links in HTML. + * Supports nomadnet://, nomadnet@, lxmf://, lxmf@ and bare */ - static renderNomadNetLinks(text) { + static renderReticulumLinks(text) { if (!text) return ""; - // Hash is 32 hex chars. Path is optional. + // Hash is 32 hex chars. Path is optional (NomadNet only). const hashPattern = "[a-fA-F0-9]{32}"; - const nomadnetRegex = new RegExp(`(?:nomadnet://)?(${hashPattern})(?::(/[\\w\\d./?%&=-]*))?`, "g"); + // This matches optional prefixes (nomadnet://, nomadnet@, lxmf://, lxmf@) + // followed by a 32-char hex hash, and an optional path starting with : + const reticulumRegex = new RegExp( + `(nomadnet://|nomadnet@|lxmf://|lxmf@)?(${hashPattern})(?::(/[\\w\\d./?%&=-]*))?`, + "g" + ); - return text.replace(nomadnetRegex, (match, hash, path) => { - const fullPath = path || "/page/index.mu"; - const url = `${hash}:${fullPath}`; - return `${match}`; + return text.replace(reticulumRegex, (match, prefix, hash, path) => { + // Determine if it should be treated as a NomadNet link: + // - Has nomadnet prefix + // - OR has a path component (e.g. hash:/page) + const isNomadNet = + (prefix && (prefix.startsWith("nomadnet://") || prefix.startsWith("nomadnet@"))) || !!path; + + if (isNomadNet) { + const fullPath = path || "/page/index.mu"; + const url = `${hash}:${fullPath}`; + return `${match}`; + } else { + // Treat as LXMF link + return `${match}`; + } }); } @@ -35,7 +51,7 @@ export default class LinkUtils { */ static renderAllLinks(text) { text = this.renderStandardLinks(text); - text = this.renderNomadNetLinks(text); + text = this.renderReticulumLinks(text); return text; } } diff --git a/meshchatx/src/frontend/js/Utils.js b/meshchatx/src/frontend/js/Utils.js index 9dfb99c..aa5adf8 100644 --- a/meshchatx/src/frontend/js/Utils.js +++ b/meshchatx/src/frontend/js/Utils.js @@ -9,7 +9,7 @@ class Utils { } static formatBytes(bytes) { - if (bytes === 0) { + if (!bytes || bytes <= 0) { return "0 Bytes"; } @@ -127,7 +127,7 @@ class Utils { } static formatBitsPerSecond(bits) { - if (bits === 0) { + if (!bits || bits <= 0) { return "0 bps"; } @@ -141,7 +141,7 @@ class Utils { } static formatBytesPerSecond(bytesPerSecond) { - if (bytesPerSecond === 0 || bytesPerSecond == null) { + if (!bytesPerSecond || bytesPerSecond <= 0) { return "0 B/s"; } @@ -155,7 +155,7 @@ class Utils { } static formatFrequency(hz) { - if (hz === 0 || hz == null) { + if (!hz || hz <= 0) { return "0 Hz"; }