-
Notifications
You must be signed in to change notification settings - Fork 297
Fix stretched/cut-off images in longform notes #3489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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]>
000db85 to
23c0caa
Compare
|
|
||
| /// 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this really necessary?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the function
| /// 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 | ||
| } | ||
|
|
There was a problem hiding this comment.
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
|
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]>


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
\nafter 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:
ensureBlockLevelImages()pre-processing to add paragraph breaks around markdown imagesKingfisherImageProviderfor MarkdownUI with proper aspect ratio and caching.fixedSizemodifier to only apply to non-longform contentSafety measures (to avoid breaking markdown structure):
Range<String.Index>consistently for correct non-ASCII handlingCloses: #1692
Checklist
Standard PR Checklist
Closes:orFixes: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:
Results:
Other notes
Known limitations (images remain inline but are handled by InlineImageProvider fallback):
![alt][id]<img>tags`code`using longer delimiters)These edge cases preserve markdown structure rather than risk breaking lists/blockquotes.
Signed-off-by: alltheseas