Skip to content

Conversation

@alltheseas
Copy link
Collaborator

@alltheseas alltheseas commented Jan 4, 2026

Summary

Images in longform markdown content were being cut off because MarkdownUI parsed them as inline elements within text paragraphs. This happened when markdown had single \n after images instead of \n\n (paragraph break).

This PR adds pre-processing to ensure standalone images are parsed as block-level elements, plus a custom Kingfisher-based image provider for proper aspect ratio handling.

Key changes:

  • Add ensureBlockLevelImages() pre-processing to add paragraph breaks around markdown images
  • Create KingfisherImageProvider for MarkdownUI with proper aspect ratio and caching
  • Move .fixedSize modifier to only apply to non-longform content

Safety measures (to avoid breaking markdown structure):

  • Skip images inside lists, blockquotes, tables, and all code block variants
  • Use Range<String.Index> consistently for correct non-ASCII handling

Closes: #1692

Checklist

Standard PR Checklist

  • I have read (or I am familiar with) the Contribution Guidelines
  • I have tested the changes in this PR
  • I have profiled the changes to ensure there are no performance regressions, or I do not need to profile the changes.
    • If not needed, provide reason: Pre-processing runs once per longform note load; regex operations are lightweight
  • I have opened or referred to an existing github issue related to this change: Stretch image rendering long form note #1692
  • My PR is either small, or I have split it into smaller logical commits that are easier to review
  • I have added the signoff line to all my commits. See Signing off your work
  • I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
  • I have added appropriate Closes: or Fixes: tags in the commit messages wherever applicable, or made sure those are not needed.

Test report

Device: iPhone 17 Pro Simulator

iOS: 26.2

Damus: 23c0caa

Setup: Opened longform notes containing images via naddr links

Steps:

  1. Launch Damus on simulator
  2. Navigate to a longform note with images
  3. Verify images display with proper aspect ratio and are not cut off

Results:

  • PASS

Other notes

Known limitations (images remain inline but are handled by InlineImageProvider fallback):

  • Multi-line list items with images on continuation lines
  • Reference-style images: ![alt][id]
  • HTML <img> tags
  • Inline code with nested backticks (e.g., `code` using longer delimiters)

These edge cases preserve markdown structure rather than risk breaking lists/blockquotes.

Signed-off-by: alltheseas

Images in longform markdown content were being cut off because they
were parsed as inline elements within text paragraphs. This happened
when markdown had single \n after images instead of \n\n (paragraph
break).

Changes:
- Add ensureBlockLevelImages() pre-processing to add paragraph breaks
  around markdown images, forcing block-level parsing
- Use Range<String.Index> consistently for correct non-ASCII handling
- Skip images in structured contexts to preserve formatting:
  - Lists (-, *, + followed by space, or numbered like "1. ")
  - Blockquotes (>)
  - Tables (lines with 2+ pipe characters)
  - Fenced code blocks (3+ backticks/tildes)
  - Indented code blocks (4 spaces or tab after blank line)
  - Inline code (matching backtick pairs)
- Handle URLs with parentheses and image titles
- Create KingfisherImageProvider for MarkdownUI with proper aspect ratio
- Move .fixedSize to only apply to non-longform content

Known limitations (images remain inline, handled by InlineImageProvider):
- Multi-line list items with images on continuation lines
- Reference-style images: ![alt][id]
- HTML <img> tags

Closes: damus-io#1692
Changelog-Fixed: Fixed stretched/cut-off images in longform notes
Signed-off-by: alltheseas <[email protected]>
@alltheseas
Copy link
Collaborator Author

alltheseas commented Jan 4, 2026

before PR

(ignore the blue line contours - I added these while debugging)
LF33

after PR

Screenshot 2026-01-03 at 7 45 22 PM

@alltheseas alltheseas force-pushed the fix/longform-image-cutoff branch from 000db85 to 23c0caa Compare January 4, 2026 06:28
@alltheseas alltheseas mentioned this pull request Jan 5, 2026
9 tasks
Comment on lines +392 to +407

/// Ensures markdown images have blank lines around them so they parse as block-level elements.
/// Without blank lines, images followed by text are parsed as inline within a paragraph,
/// causing them to be clipped by SwiftUI's Text view.
///
/// Safety: This function excludes:
/// - Fenced code blocks (``` or ~~~), indented code blocks (4 spaces/tab), and inline code
/// - Images inside lists (lines starting with -, *, + followed by space)
/// - Images inside blockquotes (lines starting with >)
/// - Images inside tables (lines containing | with table structure)
///
/// Known limitations (images remain inline, may clip if mixed with text):
/// - Multi-line list items with images on continuation lines
/// - Reference-style images: ![alt][id]
/// - HTML <img> tags
/// - Inline code with nested backticks (e.g., `` `code` `` using longer delimiters)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean docstring coverage?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the function

Comment on lines +472 to +494
/// Checks if the position is inside a list, blockquote, or table context.
/// Returns true if modifying this position would break markdown structure.
private static func isInStructuredContext(_ markdown: String, at position: String.Index) -> Bool {
// Find the start of the current line
var lineStart = position
while lineStart > markdown.startIndex {
let prevIndex = markdown.index(before: lineStart)
if markdown[prevIndex] == "\n" {
break
}
lineStart = prevIndex
}

// Get the line prefix (content before the image on this line)
let linePrefix = String(markdown[lineStart..<position])
let trimmedPrefix = linePrefix.trimmingCharacters(in: .whitespaces)

// Check for list markers: -, *, + followed by space (to avoid false positives)
if (trimmedPrefix.hasPrefix("- ") || trimmedPrefix.hasPrefix("* ") ||
trimmedPrefix.hasPrefix("+ ")) {
return true
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the point of all this

@danieldaquino danieldaquino added the pr-in-queue This PR is waiting in a queue behind their other PRs marked with the label `pr-active-review`. label Jan 5, 2026
@jb55 jb55 added pr-in-merge-queue The PR has been reviewed, tested, and added to an integration branch. Will be merged soon. and removed pr-in-queue This PR is waiting in a queue behind their other PRs marked with the label `pr-active-review`. labels Jan 6, 2026
@jb55
Copy link
Collaborator

jb55 commented Jan 6, 2026

jb55 pushed a commit to jb55/damus that referenced this pull request Jan 6, 2026
Pre-process markdown with ensureBlockLevelImages() to add paragraph breaks
around standalone images, forcing proper block-level parsing. Creates
KingfisherImageProvider for MarkdownUI to handle proper aspect ratio and
image caching.

Changelog-Fixed: Fixed stretched/cut-off images in longform notes
Closes: damus-io#3489
Closes: damus-io#3496
Signed-off-by: alltheseas <[email protected]>
Signed-off-by: William Casarin <[email protected]>
@jb55 jb55 mentioned this pull request Jan 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

longform pr-in-merge-queue The PR has been reviewed, tested, and added to an integration branch. Will be merged soon.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stretch image rendering long form note

3 participants