Skip to content

fix(content): resolve abutting nostr tags and suppress bech32 hyphenation#265

Open
dmnyc wants to merge 2 commits into
mainfrom
fix/tag-text-abutting
Open

fix(content): resolve abutting nostr tags and suppress bech32 hyphenation#265
dmnyc wants to merge 2 commits into
mainfrom
fix/tag-text-abutting

Conversation

@dmnyc

@dmnyc dmnyc commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Closes #248
Closes #316

Summary

  • Case-sensitive bech32 bodies (ContentParser) — wraps bech32 body patterns in (?-i:…) so the outer .caseInsensitive flag no longer lets [a-z0-9] absorb uppercase letters abutting a URI (e.g. nostr:nprofile1…Bitcoin was previously swallowed into one failing token)
  • Progressive-trim fallback (ContentParser) — when a token fails bech32 checksum, trims one character at a time from the end until the prefix validates; recovers the first valid entity from concatenated bech32 strings (two nprofile URIs run together with no separator) and silently discards the broken tail
  • Uppercase separator (ComposeViewModel) — the post-publish separator pass now inserts a space before A–Z in addition to a–z / 0–9, so the client itself never emits a nostr: URI abutting a capitalized word
  • No injected hyphens (RichInlineTextView) — hyphenationFactor = 0 on the shared paragraph style prevents SwiftUI from breaking unresolved bech32 fallback text with stray hyphens when it wraps across lines
  • Pill background hugs glyphs (WispPillLayoutManager) — the composer's mention-pill chip used to extend its background rect 4pt past the last glyph on each side, which at body font size completely covered the trailing ~4pt-wide space character and made @Name has receipts look like @Namehas receipts. The horizontal inset is now 0 and vertical bumped to -2, so the chip still reads as a rounded capsule but adjacent spaces render visibly outside it.

Screenshots

Reference event: nevent1qqsqqq9tsx4ws23w7x97edm7k3neud6uz422yqhjg62zzlagazzr0gcprpmhxue69uhhyetvv9ujuumwdae8gtnnda3kjctvqgsr29c6mrxlzaa20qvw8q63pjqjz4n46uaztg2pwsexw79z4hnxywsv9x052

Before
image

After
image

Test plan

  • Search for benchmark note nevent1qqsqqq9tsx4ws23w7x97edm7k3neud6uz422yqhjg62zzlagazzr0gcprpmhxue69uhhyetvv9ujuumwdae8gtnnda3kjctvqgsr29c6mrxlzaa20qvw8q63pjqjz4n46uaztg2pwsexw79z4hnxywsv9x052 — first mention resolves to @mention, no raw bech32 visible, no injected hyphens
  • Compose a note mentioning a user whose display name starts with an uppercase letter typed directly after the pill — verify published content has a separating space
  • Compose a note with two back-to-back mention pills — verify both resolve in the feed
  • Find a note where a nostr: URI abuts a capitalized word — verify the mention resolves and the word appears as separate text
  • Compose a note with a @mention followed by more text — verify the space between the pill and the next word is clearly visible (not covered by the pill background)

…tion

- ContentParser: wrap bech32 body patterns in (?-i:…) so the outer
  case-insensitive flag no longer lets [a-z0-9] absorb uppercase letters
  that immediately follow a URI (e.g. "nostr:nprofile1…Bitcoin")
- ContentParser: progressive-trim fallback — when a token fails bech32
  checksum, trim one char at a time from the end until the prefix
  validates; recovers the first valid entity from concatenated bech32
  strings (two nprofile URIs run together with no separator) and silently
  discards the broken tail instead of rendering the full mangled string
- ComposeViewModel: separator pass now inserts a space before uppercase
  letters (A–Z) in addition to lowercase/digits, so published notes never
  emit a nostr: URI abutting a capitalized word
- RichInlineTextView: hyphenationFactor = 0 on the shared paragraph style
  prevents SwiftUI from injecting hyphens into unresolved bech32 fallback
  text when it wraps across lines

Closes #248
…sible

The mention-pill chip drawn by `WispPillLayoutManager.drawBackground`
extended its background rect 4pt past the last glyph on each side
(`rect.insetBy(dx: -4, dy: -1)`). At body font size, the trailing space
character after a pill is only ~4pt wide — so the pill's overshoot
completely covered the separating space, making `@DarthCoin has receipts`
read visually as `@DarthCoinhas receipts` even though the underlying
string was correct. Users had no reliable cue to tell whether they'd
actually inserted a space between the pill and the next word, which
caused accidental run-on text in published notes.

Drop the horizontal inset to 0 (background ends at glyph bounds) and
bump the vertical inset to -2 so the chip still reads as a rounded
capsule. Now any space character after the pill renders visibly outside
the chip's background.

Closes #316.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant