Skip to content

Parse and linkify NomadNet page addresses in chat - #38

Merged
torlando-tech merged 2 commits into
mainfrom
feat/nomadnet-link-parsing
Jun 2, 2026
Merged

Parse and linkify NomadNet page addresses in chat#38
torlando-tech merged 2 commits into
mainfrom
feat/nomadnet-link-parsing

Conversation

@torlando-tech

Copy link
Copy Markdown
Owner

What

Detect bare NomadNet page addresses in chat message bodies and render them as tappable links alongside http(s) URLs — parity with Columba's chat link handling.

On-wire format (no nomadnetwork:// scheme): <32-hex-hash>:/page/path.mu, optionally followed by a backtick field/query tail, e.g.

9ce92808be498e9e05590ff27cbfdfe4:/page/forum/thread.mu`cat=help|thread=how-to-rngit

Behaviour

  • Detection + linkify — new pure ChatLinks.kt does unified web + NomadNet detection with overlap resolution. NOMADNET_ADDRESS matches <hash>:/path including the backtick =/| field tail; a bare 32-hex hash with no :/path is deliberately not linkified (those are commonly pasted destination hashes).
  • Tap → hand-off — tapping a NomadNet link fires an ACTION_VIEW intent for nomadnetwork://<address>, handled by an installed NomadNet-capable app (Columba, Sideband). With no handler installed we toast instead of crashing. Web links keep LinkAnnotation.Url → system browser.
  • No in-app NomadNet browser is added; eridanus only detects + hands off.

Tests

ChatLinksTest (10): the address regex, the backtick field tail (the exact on-wire query form), bare-hash rejection, trailing-punctuation trimming, and web/NomadNet coexistence. Full :app unit suite green.

Companion PR

The receiving side (Columba) had a bug parsing these field params on deep-link entry — fixed in torlando-tech/columba#998.

🤖 Generated with Claude Code

Detect bare NomadNet page addresses (<32-hex-hash>:/path.mu, optionally
followed by a backtick `field=value|... query tail) in message bodies and
render them as tappable links alongside http(s) URLs. Tapping fires an
ACTION_VIEW intent for nomadnetwork://<address>, handed off to an installed
NomadNet-capable app (Columba, Sideband); with no handler installed we toast
instead of crashing.

- New pure ChatLinks.kt: unified web + NomadNet link detection with overlap
  resolution (NOMADNET_ADDRESS regex + toNomadNetUri()).
- ChatScreen LinkifiedText/buildMessageBody render NomadNet spans as a tappable
  LinkAnnotation.Clickable; http(s) links keep LinkAnnotation.Url.
- ChatLinksTest covers the address regex, the backtick field tail, bare-hash
  rejection, and web/NomadNet coexistence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds detection and tap-to-open support for bare NomadNet page addresses in chat message bodies, bringing parity with Columba's link handling. A new ChatLinks.kt module centralises web and NomadNet span detection with greedy overlap resolution, while ChatScreen.kt wires the tap handler via ACTION_VIEW with an ActivityNotFoundException guard.

  • ChatLinks.kt — introduces NOMADNET_ADDRESS regex (32-hex hash + :/path, optional backtick field tail), detectChatLinks overlap resolver, splitWebUrlTrailing, and toNomadNetUri; the old per-file URL_REGEX and URL_TRAILING_TRIM constants are consolidated here.
  • ChatScreen.ktLinkifiedText gains a nomadnetListener remembered per LocalContext; buildMessageBody switches from a single-kind URL loop to a when-dispatched render pass covering WEB, NOMADNET, and mention spans.
  • ChatLinksTest.kt — 12 unit tests pin the regex edge cases (bare-hash rejection, longer-hex non-match, backtick tail capture, trailing-punctuation exclusion, web/NomadNet coexistence, and overlap precedence).

Confidence Score: 5/5

Safe to merge — the change is additive, confined to the chat render path, and carries no risk of data loss or crashes beyond the already-guarded intent dispatch.

The regex logic is sound and verified by 12 dedicated unit tests. Overlap resolution in detectChatLinks is greedy and provably correct. The ActivityNotFoundException guard prevents crashes on devices without a NomadNet app. The remember keying in LinkifiedText correctly invalidates the cached AnnotatedString when context changes. No pre-existing paths are altered beyond the URL-detection loop, which is a straight refactor with equivalent cursor accounting.

No files require special attention.

Important Files Changed

Filename Overview
app/src/main/kotlin/tech/torlando/eridanus/ui/screens/ChatLinks.kt New file introducing NOMADNET_ADDRESS regex, detectChatLinks overlap resolver, splitWebUrlTrailing, and toNomadNetUri. Logic is correct: lookbehind prevents over-matching on longer hex runs, greedy overlap resolution is sound, trailing-punctuation handling via lookbehind backtracking works correctly.
app/src/main/kotlin/tech/torlando/eridanus/ui/screens/ChatScreen.kt Refactors LinkifiedText and buildMessageBody to handle both WEB and NOMADNET kinds via detectChatLinks; adds openNomadNetLink with ActivityNotFoundException guard. remember keys are correctly threaded; cursor accounting in the render loop is equivalent to the old code.
app/src/test/kotlin/tech/torlando/eridanus/ui/screens/ChatLinksTest.kt 12 unit tests covering detection, backtick field tail, bare-hash rejection, longer-hex rejection, trailing-punctuation exclusion, web/NomadNet coexistence, overlap precedence, and splitWebUrlTrailing. All critical paths are pinned.

Sequence Diagram

sequenceDiagram
    participant User
    participant LinkifiedText
    participant detectChatLinks
    participant buildMessageBody
    participant openNomadNetLink
    participant ExternalApp

    User->>LinkifiedText: compose message with NomadNet address
    LinkifiedText->>detectChatLinks: text
    detectChatLinks-->>LinkifiedText: "List<ChatLink> (WEB + NOMADNET, overlap-resolved)"
    LinkifiedText->>buildMessageBody: text, links, nomadnetListener
    buildMessageBody-->>LinkifiedText: AnnotatedString (LinkAnnotation.Url / Clickable spans)
    User->>LinkifiedText: tap NomadNet link span
    LinkifiedText->>openNomadNetLink: nomadnetwork://hash:/path URI
    alt NomadNet app installed
        openNomadNetLink->>ExternalApp: ACTION_VIEW intent
    else no handler
        openNomadNetLink-->>User: Toast "No NomadNet app installed"
    end
Loading

Reviews (2): Last reviewed commit: "address greptile review feedback (greplo..." | Re-trigger Greptile

Comment thread app/src/main/kotlin/tech/torlando/eridanus/ui/screens/ChatScreen.kt
Comment thread app/src/main/kotlin/tech/torlando/eridanus/ui/screens/ChatLinks.kt Outdated
- detectChatLinks no longer returns its internal working list for the
  0/1-candidate case; the dedup loop now handles those too, so callers
  always get a freshly-built result list.
- Extract the web-URL trailing-trim into a shared, tested helper
  splitWebUrlTrailing() in ChatLinks.kt (with URL_TRAILING_TRIM), used by
  buildMessageBody; adds pinning tests for the trim behaviour.

(The "No NomadNet app installed" Toast string is left inline to match the
codebase, which hardcodes all user-facing Toast strings.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@torlando-tech
torlando-tech merged commit 0ee883e into main Jun 2, 2026
2 checks passed
@torlando-tech
torlando-tech deleted the feat/nomadnet-link-parsing branch June 2, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant