Conversation
…eprocessing This commit introduces expandable "About" and "What's New" sections in the repository details view to handle long READMEs and release notes. It also significantly enhances the Markdown preprocessing logic to better handle HTML-in-Markdown and GitHub-specific formatting. - **feat(details)**: Introduced `ExpandableMarkdownContent` component to provide "Read More" and "Show Less" functionality for long text blocks. - **feat(details)**: Added `isAboutExpanded` and `isWhatsNewExpanded` states to `DetailsState` along with corresponding toggle actions in `DetailsViewModel`. - **refactor(data)**: Extensively updated `preprocessMarkdown.kt` to handle various HTML tags (e.g., `<picture>`, `<video>`, `<a>`, headings, inline formatting) and convert them to standard Markdown. - **refactor(data)**: Improved URL resolution in Markdown to correctly handle relative paths and GitHub blob links. - **feat(ui)**: Integrated content size animations and gradient overlays for collapsed states in the details sections. - **i18n**: Added `read_more` and `show_less` string resources.
This commit adds localized string resources for the "Read More" and "Show Less" labels across several languages to support UI components with expandable text. - **i18n**: Added `read_more` and `show_less` string resources to Turkish (tr), Spanish (es), Chinese (zh-rCN), French (fr), Hindi (hi), Italian (it), Japanese (ja), Korean (kr), Polish (pl), Bengali (bn), and Russian (ru) locales.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
WalkthroughAdds localized "read more"/"show less" strings, introduces expand/collapse state and actions, refactors Details UI to render expandable Markdown for About/WhatsNew, and substantially enhances Markdown/HTML preprocessing for richer URL/image handling. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as DetailsScreen (Composable)
participant VM as DetailsViewModel
participant Data as PreprocessMarkdown
User->>UI: open Details screen
UI->>VM: load details
VM->>Data: preprocessMarkdown(readme, baseUrl)
Data-->>VM: markdownContent
VM-->>UI: emit state + markdownContent
UI->>UI: render ExpandableMarkdownContent(collapsed)
User->>UI: tap "Read more"
UI->>VM: DetailsAction.ToggleAboutExpanded
VM-->>VM: toggle isAboutExpanded
VM-->>UI: updated state (isAboutExpanded = true)
UI->>UI: animate expand, show "Show less"
User->>UI: tap "Show less"
UI->>VM: DetailsAction.ToggleAboutExpanded
VM-->>VM: toggle isAboutExpanded
VM-->>UI: updated state (isAboutExpanded = false)
UI->>UI: animate collapse, show "Read more"
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt (1)
251-259: Consider expanding HTML entity decoding.The current set covers common entities, but GitHub READMEs may contain others like
—,–,…, or numeric entities ( ). This can be deferred if not causing issues currently.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt` around lines 251 - 259, The HTML entity decoding in preprocessMarkdown.kt only handles a few entities via chained .replace on the processed string; extend decoding to include common README entities (—, –, …, ™, etc.) and numeric entities (decimal like   and hex like —) by adding replacements for those named entities and a small routine to parse/replace numeric entities (&#\d+; and &#x[0-9A-Fa-f]+;) into their Unicode code points; update the code that operates on the processed variable (the replace chain in the preprocessMarkdown function) to include these extra named replacements and a numeric-entity decoder to ensure broader coverage on GitHub READMEs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt`:
- Around line 103-122: The video-handling replace blocks use raw src values so
relative video URLs aren't resolved; update both Regex replacement lambdas in
preprocessMarkdown (the two blocks that assign to processed using Regex for
<video> and for <video> with <source>) to call resolveUrl(src) and use the
resolved value when building the markdown link (e.g., val resolved =
resolveUrl(src) and then "[Video]($resolved)") instead of using
match.groupValues[2] directly; keep the same match and group extraction logic
but replace src with the resolved URL.
- Around line 23-33: The resolveUrl function currently uses
path.trim().trimStart('.', '/') which strips leading ".." and breaks
parent-directory references; replace that trimming with logic that only removes
leading "./" segments and any leading slashes but preserves "../" sequences.
Specifically, in resolveUrl (and where normalizedBaseUrl is used) trim
whitespace, then repeatedly remove only "./" prefixes (not ".."), then remove
leading '/' characters (e.g., with a loop or regex that matches ^(?:\./)+ and
then ^/+), and finally concatenate "$normalizedBaseUrl$cleaned" so
"../images/logo.png" remains intact while "./img.png" and "/img.png" are
normalized.
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt`:
- Around line 115-120: The collapse-height logic uses maxHeight (unbounded in
lazy lists) and the clipped measurement so needsExpansion becomes unstable;
replace the dynamic collapsedHeightDp = maxHeight * 0.7f with a fixed dp
constant (e.g. val collapsedHeightDp = 200.dp) and convert it to px via
with(density) { collapsedHeightDp.toPx() }, and stabilize measurements by
measuring the full content height instead of the clipped height: move the
onGloballyPositioned that sets contentHeightPx to a wrapper that is not clipped
(or use SubcomposeLayout/MeasurePolicy to measure the content unconstrained) so
contentHeightPx captures the intrinsic full content height; update the symbols
collapsedHeightDp, collapsedHeightPx, contentHeightPx and needsExpansion
accordingly (also apply the same fix to the similar blocks at the other
occurrences referenced).
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt`:
- Around line 127-134: In WhatsNew.kt replace the image transformer passed to
the Markdown composable (currently Coil3ImageTransformerImpl) with the app's
custom MarkdownImageTransformer so the Details module renders images the same as
About; locate the Markdown call in the WhatsNew composable and swap the
imageTransformer parameter to MarkdownImageTransformer, ensuring any
imports/constructor usage match the existing MarkdownImageTransformer
implementation that handles GitHub URLs and filters SVGs.
---
Nitpick comments:
In
`@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt`:
- Around line 251-259: The HTML entity decoding in preprocessMarkdown.kt only
handles a few entities via chained .replace on the processed string; extend
decoding to include common README entities (—, –, …, ™,
etc.) and numeric entities (decimal like   and hex like —) by adding
replacements for those named entities and a small routine to parse/replace
numeric entities (&#\d+; and &#x[0-9A-Fa-f]+;) into their Unicode code points;
update the code that operates on the processed variable (the replace chain in
the preprocessMarkdown function) to include these extra named replacements and a
numeric-entity decoder to ensure broader coverage on GitHub READMEs.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xmlcore/presentation/src/commonMain/composeResources/values-es/strings-es.xmlcore/presentation/src/commonMain/composeResources/values-fr/strings-fr.xmlcore/presentation/src/commonMain/composeResources/values-hi/strings-hi.xmlcore/presentation/src/commonMain/composeResources/values-it/strings-it.xmlcore/presentation/src/commonMain/composeResources/values-ja/strings-ja.xmlcore/presentation/src/commonMain/composeResources/values-kr/strings-kr.xmlcore/presentation/src/commonMain/composeResources/values-pl/strings-pl.xmlcore/presentation/src/commonMain/composeResources/values-ru/strings-ru.xmlcore/presentation/src/commonMain/composeResources/values-tr/strings-tr.xmlcore/presentation/src/commonMain/composeResources/values-zh-rCN/strings-zh-rCN.xmlcore/presentation/src/commonMain/composeResources/values/strings.xmlfeature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsAction.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsState.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
Show resolved
Hide resolved
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
Show resolved
Hide resolved
...entation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt
Outdated
Show resolved
Hide resolved
...ation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
Outdated
Show resolved
Hide resolved
...ation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
Outdated
Show resolved
Hide resolved
This commit significantly improves the Markdown preprocessing for repository descriptions and READMEs. It introduces more robust URL resolution, better handling of HTML-to-Markdown conversions, and filters out SVGs/badges to keep the UI clean. Additionally, it refines the "Read More" expansion logic in the UI by centralizing height constraints. - **feat(data)**: Overhauled `preprocessMarkdown` with support for reference-style links/images, better HTML tag stripping (e.g., `<picture>`, `<iframe>`, `<summary>`), and enhanced URL normalization. - **feat(data)**: Added detection for common badge providers (Shields.io, Codecov, etc.) and SVG badges to exclude them from Markdown rendering. - **refactor(ui)**: Moved `collapsedHeight` calculation to `DetailsRoot` and passed it down to `About` and `WhatsNew` sections for consistent expansion behavior. - **refactor(ui)**: Replaced `BoxWithConstraints` with a more stable `onGloballyPositioned` approach in `About` and `WhatsNew` to prevent layout flickering during expansion. - **fix(ui)**: Improved the "Read More" logic to ensure the content height is correctly measured and the fade-out gradient matches the container's background color. - **chore**: Introduced `MarkdownImageTransformer` as a unified image transformer for Markdown content.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt (1)
409-416: Numeric entity decoding limited to ASCII printable range.The check
code in 32..126excludes common entities like (non-breaking space),—(em dash), and’(curly quote). These will be left as-is in the output.♻️ Optional: Expand to valid Unicode range
processed = processed.replace(Regex("""&#(\d+);""")) { match -> val code = match.groupValues[1].toIntOrNull() - if (code != null && code in 32..126) { + if (code != null && code in 32..0x10FFFF && code.toChar().isDefined()) { code.toChar().toString() } else { match.value } }Alternatively, handle hex entities too:
&#x...;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt` around lines 409 - 416, The numeric entity decoder in preprocessMarkdown.kt currently only decodes decimal entities in the ASCII printable range via the processed.replace(Regex("""&#(\d+);""")) block; update it to decode both decimal and hex entities (handle patterns like &#x...;) and accept the full Unicode range (0..0x10FFFF). Parse the matched number with the appropriate radix (10 for decimal, 16 for hex), validate it is within 0..0x10FFFF, and convert to a string using Character.toChars(code) for codepoints > 0xFFFF (or toChar() for BMP) before returning it; otherwise return the original match.value. Ensure the regex and replacement logic reference the same match groups you update so both decimal and hex forms are handled.feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt (1)
115-177: Consider reusingExpandableMarkdownContentto remove duplicated expand/collapse logic.This block is nearly the same behavior already implemented in
About.kt. Reusing that composable (with card-specific colors/modifier inputs) will reduce drift and future bug fixes in two places.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt` around lines 115 - 177, The current expand/collapse UI block duplicates logic present in About.kt; replace the Column/Box/TextButton section with a call to the shared ExpandableMarkdownContent composable to remove duplication. Pass the release.description (falling back to stringResource(Res.string.no_release_notes)), the same colors/typography/flavour/imageTransformer (MarkdownImageTransformer), the liquidState modifier, the collapsedHeight/content measurement hooks (or collapsedHeight and a way for the shared composable to measure contentHeightPx), and styling inputs such as cardColor and modifiers; wire the existing isExpanded/needsExpansion state and onToggleExpanded callback into ExpandableMarkdownContent so the gradient overlay and the “Read more” / “Show less” button are handled by the shared component instead of duplicating the Box + TextButton logic in WhatsNew.kt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt`:
- Around line 323-337: The anchor-link conversion currently inserts the raw
href, causing relative links to break; update the replacement lambda in the
Regex that converts <a ...>...</a> to call the existing resolveUrl(...) helper
on match.groupValues[2] (the url) and use the resolved URL in the output (e.g.,
val resolved = resolveUrl(url); then produce "[$text]($resolved)" or
"[$resolved]($resolved)" for empty text) so anchor links are resolved
consistently with images and videos.
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt`:
- Around line 119-124: The contentHeightPx state should be reset when the
displayed content or collapsedHeight changes: update the remember call that
initializes contentHeightPx to include content and collapsedHeightPx as keys
(remember(content, collapsedHeightPx) { mutableStateOf(0f) }) so measurements
don't become stale after shrink/replace; also remove the remember wrapper around
needsExpansion and compute needsExpansion directly as val needsExpansion =
contentHeightPx > collapsedHeightPx && collapsedHeightPx > 0f to avoid
memoization using outdated values.
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt`:
- Around line 109-113: The contentHeightPx state is being remembered globally
and not reset when the release content changes, causing needsExpansion to stay
true for shorter releases; update the state creation so contentHeightPx is
remembered per-release (e.g., remember(release.id) { mutableStateOf(0f) } or
otherwise keyed to the content) and ensure any other occurrences (the second
block around needsExpansion at lines ~136-140) use the same per-release keyed
remember; this guarantees contentHeightPx resets when the release changes and
prevents stale expansion toggles.
---
Nitpick comments:
In
`@feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt`:
- Around line 409-416: The numeric entity decoder in preprocessMarkdown.kt
currently only decodes decimal entities in the ASCII printable range via the
processed.replace(Regex("""&#(\d+);""")) block; update it to decode both decimal
and hex entities (handle patterns like &#x...;) and accept the full Unicode
range (0..0x10FFFF). Parse the matched number with the appropriate radix (10 for
decimal, 16 for hex), validate it is within 0..0x10FFFF, and convert to a string
using Character.toChars(code) for codepoints > 0xFFFF (or toChar() for BMP)
before returning it; otherwise return the original match.value. Ensure the regex
and replacement logic reference the same match groups you update so both decimal
and hex forms are handled.
In
`@feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt`:
- Around line 115-177: The current expand/collapse UI block duplicates logic
present in About.kt; replace the Column/Box/TextButton section with a call to
the shared ExpandableMarkdownContent composable to remove duplication. Pass the
release.description (falling back to
stringResource(Res.string.no_release_notes)), the same
colors/typography/flavour/imageTransformer (MarkdownImageTransformer), the
liquidState modifier, the collapsedHeight/content measurement hooks (or
collapsedHeight and a way for the shared composable to measure contentHeightPx),
and styling inputs such as cardColor and modifiers; wire the existing
isExpanded/needsExpansion state and onToggleExpanded callback into
ExpandableMarkdownContent so the gradient overlay and the “Read more” / “Show
less” button are handled by the shared component instead of duplicating the Box
+ TextButton logic in WhatsNew.kt.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsRoot.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.ktfeature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
feature/details/data/src/commonMain/kotlin/zed/rainxch/details/data/utils/preprocessMarkdown.kt
Show resolved
Hide resolved
...entation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/About.kt
Outdated
Show resolved
Hide resolved
...ation/src/commonMain/kotlin/zed/rainxch/details/presentation/components/sections/WhatsNew.kt
Show resolved
Hide resolved
…lity This commit fixes issues where UI sections might not correctly recalculate their expandable heights when content changes and improves markdown link resolution. - **fix(details)**: Updated `WhatsNew` and `About` sections to re-calculate `contentHeightPx` when the source content or collapsed height constraints change by adding appropriate keys to `remember`. - **fix(details)**: Optimized `About` section by removing redundant `remember` for `needsExpansion` and switching to `mutableFloatStateOf` in `WhatsNew` for better performance. - **fix(data)**: Enhanced markdown preprocessing to resolve relative or shorthand URLs within markdown link syntax.
Summary by CodeRabbit