Add LXMF link handling and improve link rendering logic

- Implemented event handling for LXMF links in ConversationViewer and RNCPPage components to navigate to message destinations.
- Updated LinkUtils to support both NomadNet and LXMF link formats, enhancing link detection and rendering.
- Modified RNPathTracePage to display error state alongside loading and result states for better user feedback.
- Improved utility functions to handle edge cases for zero or negative values in various formatting methods.
This commit is contained in:
Sudo-Ivan
2026-01-16 08:53:11 -06:00
parent 81b574da29
commit 8e47de38ad
5 changed files with 55 additions and 15 deletions
@@ -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 {
@@ -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 },
});
}
}
},
},
};
@@ -69,7 +69,7 @@
</div>
<!-- results area -->
<div v-if="traceResult || isLoading" class="space-y-6">
<div v-if="traceResult || isLoading || error" class="space-y-6">
<!-- loading state -->
<div v-if="isLoading" class="glass-card p-12 flex flex-col items-center justify-center gap-4">
<div class="relative">
+26 -10
View File
@@ -1,19 +1,35 @@
export default class LinkUtils {
/**
* Detects and wraps NomadNet links in HTML.
* Supports nomadnet://<hash>:/path and <hash>:/path
* Detects and wraps Reticulum (NomadNet and LXMF) links in HTML.
* Supports nomadnet://<hash>, nomadnet@<hash>, lxmf://<hash>, lxmf@<hash> and bare <hash>
*/
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 `<a href="#" class="nomadnet-link text-blue-600 dark:text-blue-400 hover:underline font-mono" data-nomadnet-url="${url}">${match}</a>`;
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 `<a href="#" class="nomadnet-link text-blue-600 dark:text-blue-400 hover:underline font-mono" data-nomadnet-url="${url}">${match}</a>`;
} else {
// Treat as LXMF link
return `<a href="#" class="lxmf-link text-blue-600 dark:text-blue-400 hover:underline font-mono" data-lxmf-address="${hash}">${match}</a>`;
}
});
}
@@ -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;
}
}
+4 -4
View File
@@ -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";
}