Skip to content

fix(crd): cut collapsed posts under the 3rd line, no fade - #10546

Open
valentinyanakiev wants to merge 3 commits into
developfrom
fix/10372-collapsed-posts-replace-the-fade
Open

valentinyanakiev wants to merge 3 commits into
developfrom
fix/10372-collapsed-posts-replace-the-fade

Conversation

@valentinyanakiev

@valentinyanakiev valentinyanakiev commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Fixes #10372

Summary

Collapsed posts and phase descriptions used to fade out with a gradient and could cut through the middle of a line, especially after a paragraph break or over images. They now end cleanly under the third rendered line of text, leading images are cropped to 160px, later images are hidden until expanded, and a plain Read more / Show less link sits below the content.

Root cause

src/crd/components/common/ExpandableMarkdown.tsx clipped content at a fixed max-height of 3 x line-height and overlaid a surface gradient, so paragraph margins or headings in the first lines shifted the cut mid-line and images were faded rather than handled.

Fix

ExpandableMarkdown now measures real rendered line boxes (measureLines) and clips directly under the maxLines-th one, only when the text exceeds it; the gradient overlay is removed. classifyMedia tags each image or embed as hero (before any text, or every one in a text-free post: shown, cropped to 160px with object-fit cover) or hidden (after text: withheld while collapsed), and any media makes the block collapsible. Expanded, the content renders as authored. Measurement re-runs via a ResizeObserver and keeps the previous state when nothing changed, so idle resize callbacks cause no re-render. The toggle is a primary-colour, medium-weight link below the content with Read more / Show less labels in all six languages. PostCard and TabStateHeader both use the component, so posts and phase descriptions get the treatment; the admin collapse setting and expanded-by-default behaviour are unchanged.

Tests

src/crd/components/common/ExpandableMarkdown.test.tsx covers the cut under the 3rd line across a paragraph break, exactly 3 lines with no toggle, hero cropping with short text, hidden trailing and in-between images restored on expand, image-only posts, embeds, inline code in headings, out-of-order table cells, linear-time measurement of very long posts, and that a resize which leaves the measurement unchanged does not re-render (fails with 'expected 5 to be 3' without the state bail-out).

Verification

  • pnpm vitest run src/crd/components/common/ExpandableMarkdown.test.tsx: 13 passed; the new re-render test failed before the fix (expected 5 to be 3)
  • pnpm vitest run (full suite): 657 files, 5735 passed, 4 skipped
  • pnpm run typecheck:native: passed
  • eslint and biome check on the touched files: clean; biome ci . reports pre-existing errors only in files this branch does not touch

Commits

  • 260d5ab fix(crd): cut collapsed posts under the 3rd line, no fade
  • 416cbd0 fix(crd): collapse embeds like images, bound line measurement
  • 9b1765f perf(crd): skip re-render when a resize leaves the post cut unchanged

Delivered by Brokkr run land-the-client-web-slice-of-wor-8f73c557: Claude implements, the repository's own gates verify offline, and Codex judges. CodeRabbit findings are answered by the run's watch loop.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

Run configuration
Commits

Reviewing files that changed from the base of the PR and between e51a429 and 9b1765f.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

ExpandableMarkdown now measures rendered text lines and media to determine collapsed overflow. It clips text below the configured line limit, crops leading media, and hides later media until expansion. TabStateHeader uses the component defaults, and space translations update the toggle labels.

Changes

Markdown truncation

Layer / File(s) Summary
Visual-line and media measurement
src/crd/components/common/ExpandableMarkdown.tsx, src/crd/components/common/ExpandableMarkdown.test.tsx
The component measures visual text lines, classifies images and embeds, and records the cut position and overflow state. Tests cover visual-line ordering, inline code, paragraph breaks, and measurement with 25,000 lines.
Collapsed preview rendering
src/crd/components/common/ExpandableMarkdown.tsx, src/crd/components/common/ExpandableMarkdown.test.tsx, src/crd/components/common/MarkdownContent.tsx, src/crd/components/common/MarkdownContent.iframe.test.tsx
Collapsed rendering clips text at the measured line boundary, crops hero media, and hides later media. The toggle appears below the content. Iframe elements and wrappers receive data-embed. Tests cover expanded and collapsed states, media handling, and embeds.
Consumer and toggle labels
src/crd/components/space/TabStateHeader.tsx, src/crd/i18n/space/space.*.json
TabStateHeader no longer passes responsive line limits or removed styling and label props. Bulgarian, German, English, Spanish, French, and Dutch translations replace readLess with showLess and remove leading ellipses from readMore.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: ccanos, bobbykolev

Merge Risk: Minimal · previous review e51a4

No merge-blocking issue was identified in the collapsed preview changes.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title names the main change: ExpandableMarkdown now cuts collapsed posts below the third line and drops the fade overlay. The changeset matches this. The summary confirms line-based clipping repla…
Linked Issues check Passed Issue #10372 is the only active direct target. Its coding requirements are met at head e51a429.

- Text cut under the 3rd line: measureLines counts rendered line boxes. cutAt ends at the bottom …
Out of Scope Changes check Passed The changes stay within the scope of #10372 or support it.

- ExpandableMarkdown.tsx: the media classification, line measurement, toggle, and resize handling implement the issue rules.
- `MarkdownCo…
Full details: Title check

Explanation

The title names the main change: ExpandableMarkdown now cuts collapsed posts below the third line and drops the fade overlay. The changeset matches this. The summary confirms line-based clipping replaces the height-based clamp and the gradient overlay is removed. The title uses a conventional commit prefix and stays under one line, so it passes.

Full details: Linked Issues check

Explanation

Issue #10372 is the only active direct target. Its coding requirements are met at head e51a429.

  • Text cut under the 3rd line: measureLines counts rendered line boxes. cutAt ends at the bottom of line maxLines. The cut applies only when textOverflows (more than maxLines lines). The gradient overlay is removed.
  • Text of 3 lines or fewer: cutAt is null and maxHeight is undefined, so the text is not cut.
  • Hero media (before any text, or every item in a text-free post): classifyMedia tags it data-collapse=hero. The collapsed classes apply max-h-[160px], overflow-hidden, and object-cover. Any media sets overflow to 'yes', so the post is collapsible even with short text.
  • Media after text: tagged data-collapse=hidden and hidden with !hidden while collapsed.
  • Expanded: isCollapsed is false, so no collapse classes or maxHeight apply. Content renders as authored.
  • No toggle: showToggle requires overflow 'yes', which requires text overflow or media.
  • Labels: the toggle uses postSnippet.readMore and postSnippet.showLess. The six locale files (bg, de, en, es, fr, nl) carry these keys.
  • Phase descriptions: TabStateHeader renders the same component with default limits.
  • No server changes are in the reviewed change set.

The incremental change since the previous review adds the resize bail-out (setMeasurement returns the previous object when unchanged). This does not alter any requirement above. Its tests are reported in the PR, but I did not run them.

Full details: Out of Scope Changes check

Explanation

The changes stay within the scope of #10372 or support it.

  • ExpandableMarkdown.tsx: the media classification, line measurement, toggle, and resize handling implement the issue rules.
  • MarkdownContent.tsx and its iframe test: data-embed marks the outer box of embeds. ExpandableMarkdown uses this marker to crop or hide embeds with the same image rules. The issue does not name embeds, but an embed after text would otherwise break the 3-line cut. This is a related extension in the same media path, not an unrelated change.
  • TabStateHeader.tsx: removes the old per-screen line limits, surface, and label props, and uses the component defaults. This implements the phase-description requirement.
  • Locale files: the postSnippet keys change to the new labels. This implements the translation requirement.
  • ExpandableMarkdown.test.tsx: covers the requirement cases, including the resize re-render case.

No unrelated product, server, or configuration change appears in the reviewed set.

Limitation: the diff for MarkdownContent.tsx from the previous head returned no text, and the diff for ExpandableMarkdown.tsx was unavailable. I read the head version of ExpandableMarkdown.tsx directly. The data-embed marker in MarkdownContent.tsx is inferred from the PR summary and from the dataset.embed check in the head code. This does not change the verdict.

Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 6 functions across 5 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 5741bb1 and e51a429.

Files selected for processing: 2
  • src/crd/components/common/ExpandableMarkdown.test.tsx
  • src/crd/components/common/ExpandableMarkdown.tsx

Included review availability: This review used your included allowance. 3 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Fix comments: Fix CodeRabbit comments on this PR

@valentinyanakiev valentinyanakiev changed the title fix(crd): clean cut-off and Read more for collapsed posts fix(crd): cut collapsed posts under the 3rd line, no fade Nov 2, 2026
valentinyanakiev and others added 3 commits November 3, 2026 14:39
Collapsed post and phase descriptions were clipped at a fixed
3 x line-height and covered with a gradient. A paragraph break in the
first lines left half a line showing, and images were smeared under
the fade.

ExpandableMarkdown now measures the rendered line boxes and cuts
directly under line N. Only text over N lines is cut. An image before
the text, or every image of an image-only post, is cropped to 160px
with object-fit cover. Images after text are hidden until expanded,
and any image makes the content collapsible. Read more / Show less
is a primary-colour link below the content. The gradient, the chip
and the now-unused surface prop are removed. Labels are updated in
all six languages.

Refs #10372

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review of the collapsed-post treatment:

- Embeds: MarkdownContent tags an embed's outer box `data-embed`, and
  ExpandableMarkdown treats it like an image - cropped to 160px before
  any text, hidden after it, and collapsible on its own. An embed with
  short text no longer escapes the collapse setting.
- Line measurement keeps only the first maxLines + 1 visual lines, so
  each text fragment costs O(maxLines) instead of a scan of every line
  so far. A 25,000-line post measured in 1.5s before, 13ms now.
- Fragments merge into one visual line when their vertical middles
  overlap, so inline code in a heading no longer counts as a 2nd line.
- The collapsed preview rules are !important, so authored inline
  display / min-height / max-height styles cannot defeat the crop or
  the hiding. Expanded, the authored styles apply again.
- Phase descriptions use the shared 3-line budget and Read more label;
  the now-unused label override props and seeFullDescription key
  (all six languages) are removed.

Refs #10372

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ResizeObserver re-measures on every callback, including the one it
fires on observe(); each call stored a fresh measurement object, so every
collapsed post and phase description committed again even when no line
moved. Keep the previous object when overflow and cut are unchanged.

Refs #10372

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@valentinyanakiev
valentinyanakiev force-pushed the fix/10372-collapsed-posts-replace-the-fade branch from e51a429 to 9b1765f Compare November 3, 2026 12:39
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.

Collapsed posts: replace the fade with a clean cut-off and a Read more link

1 participant