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";
}