Skip to content

fix(app): paginate release notes by version instead of bullet - #400

Merged
Astro-Han merged 6 commits into
devfrom
pawwork/fix-release-notes-pagination
May 3, 2026
Merged

fix(app): paginate release notes by version instead of bullet#400
Astro-Han merged 6 commits into
devfrom
pawwork/fix-release-notes-pagination

Conversation

@Astro-Han

@Astro-Han Astro-Han commented May 3, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the release notes pagination regression where each bullet was rendered as a separate toast page. Now each release version maps to exactly one page, with all bullets merged into a scrollable description.

Why

Issue #398: PR #372 introduced a behavior regression where DialogReleaseNotes paginated by individual bullet items instead of by version. A release with 7 bullets would show 7 toast pages. The intended behavior is one page per version.

Related Issue

Closes #398

Human Review Status

Pending. A human should make the final merge decision after reviewing the final diff and verification evidence.

Review Focus

  1. Data boundary correctness: parseRelease body path now returns exactly one Highlight per release version, while the structured highlights schema path remains unchanged with its own cap.
  2. Cap separation: sliceHighlights now tracks releaseBodyPages and structuredHighlights separately. Release body is capped at 5 version pages; structured schema keeps its 15-item cap.
  3. UI scroll behavior: The description area in DialogReleaseNotes is independently scrollable with whitespace-pre-line preserving bullet line breaks. Title and buttons stay fixed. Added tabIndex={0}, role="region", and aria-labelledby for keyboard accessibility.

Risk Notes

  • Paragraph-only notices (non-bullet) remain without forced prefix due to the ParsedNotice discriminated union.
  • Very long bullet lists within a single version page are scrollable rather than paginated. This is the intended UX change.
  • Structured highlights schema behavior is preserved; a regression test ensures 6+ items are not truncated by the 5-page release-body cap.

How To Verify

Focused tests: 22 passed, 0 failed
TypeScript typecheck: passed

From repo root:

bun --cwd packages/app test --preload ./happydom.ts ./src/context/highlights.test.ts
bun --cwd packages/app typecheck

Manual UI Check

  • Simulated one release with 6 bullets.
  • Confirmed all bullets render on one page with prefix and line breaks.
  • Confirmed long description scrolls independently while title/buttons stay fixed.
  • Confirmed no per-bullet pagination dots are created.
  • Confirmed description region is keyboard-focusable.

Checklist

  • Human review status is stated above as pending, approved, or not required
  • I linked the related issue, or stated why there is no issue
  • This PR has type, scope, and priority labels, or I requested maintainer labeling
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes when needed, with screenshots or recordings
  • I considered macOS and Windows impact for desktop, packaging, updater, signing, paths, shell, or permissions changes
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • Style

    • Release notes dialog redesigned with improved scrolling behavior and layout organization; added accessibility enhancements
  • Refactor

    • Enhanced release highlights parsing and formatting for improved presentation of bullet points and sections

- Change MAX_RELEASE_HIGHLIGHTS from 15 to 5 (version pages, not bullets)
- Introduce ParsedNotice type to distinguish bullets from summary paragraphs
- Refactor parseNoticeDescriptions to return ParsedNotice | undefined
- Add formatReleaseNoticeDescription to merge bullets with • prefix
- Update parseRelease body path: one version = one Highlight
- Keep structured highlights schema behavior unchanged
- Make DialogReleaseNotes description scrollable with whitespace-pre-line
- Fix title and buttons to stay fixed while description scrolls
- Update tests to expect merged bullets per version page
- Add regression tests for version granularity and structured schema

Closes #398
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The head commit changed during the review from f840915 to f095785.

📝 Walkthrough

Walkthrough

Release-note parsing now aggregates bullets per release into a single bullet-prefixed, newline-joined description and enforces separate caps: 5 release-version pages and 15 structured highlights. The release-notes dialog was refactored so the description preserves newlines, scrolls internally, and resets scroll on page changes. Tests updated accordingly.

Changes

Release Highlights Aggregation & Dialog Scroll

Layer / File(s) Summary
Data Shape / Limits
packages/app/src/context/highlights.tsx
Replaces MAX_RELEASE_HIGHLIGHTS with MAX_RELEASE_VERSION_PAGES = 5 and MAX_STRUCTURED_HIGHLIGHTS = 15; ParsedRelease now includes `source: "release-body"
Parsing / Formatting
packages/app/src/context/highlights.tsx
Adds ParsedNotice union and parseNoticeContent(...); parseReleaseBodyNotice(...) returns ParsedNotice; formatReleaseNoticeDescription(...) renders bullets as lines and joins with \n. Release-body parsing now yields a single Highlight per release.
Selection / Slicing
packages/app/src/context/highlights.tsx
sliceHighlights(...) reworked to iterate selected releases, apply per-source caps while accumulating highlights, then dedupe the final list via dedupeKey(...); removed post-dedupe hard slice to previous max.
UI Layout / Accessibility
packages/app/src/components/dialog-release-notes.tsx
Left column converted to column flex; description element now uses whitespace-pre-line, overflow-y-auto, flex-1, min-h-0; accessibility attributes added (role="region", aria-labelledby, tabIndex={0}); createEffect resets description scrollTop to 0 on index() change; bottom controls set to shrink-0.
Tests
packages/app/src/context/highlights.test.ts
Expectations updated for merged bullet descriptions (-prefixed, newline-joined), localized fallback aggregation, preserved wrapped continuations, heading-stop parsing, skipped-version range capped at 5 version pages, structured highlights produced per-item and capped at 15, and mixed-content notices preserved with intro plus bullets.

Sequence Diagram

sequenceDiagram
  participant User
  participant Dialog as DialogReleaseNotes
  participant Context as highlights.tsx
  participant Data as ReleaseSource
  User->>Dialog: open release notes
  Dialog->>Context: request sliced highlights
  Context->>Data: fetch/parse releases
  Data-->>Context: parsed releases (structured / release-body)
  Context->>Context: aggregate bullets per release, apply caps, dedupe
  Context-->>Dialog: highlights (per release-body page or structured items)
  Dialog->>Dialog: render title + scrollable description
  User->>Dialog: navigate pages
  Dialog->>Dialog: reset description scrollTop to 0 (via createEffect)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Possibly related issues

  • #398 — Task to merge release bullets per version into a single toast page; this PR implements that behavior.
  • Astro-Han/pawwork#163 — Related localization/aggregation changes for Chinese release-body parsing and fallback handling.

Suggested labels

bug, P2, app, ui

Poem

🐰 Bullets once hopped, one-per-page parade,
Now they cuddle close in a single cascade.
With and newlines they settle in view,
Scrolls tucked inside, buttons steady and true.
Hooray for the notes — neat, whole, and new 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: fixing release notes pagination to be at the version level rather than per bullet.
Description check ✅ Passed The PR description follows the template structure with Summary, Why, Related Issue, Human Review Status, Review Focus, Risk Notes, How To Verify, Manual UI Check, and Checklist sections.
Linked Issues check ✅ Passed All coding objectives from #398 are met: parseRelease merges bullets into one Highlight per version, cap separation implemented (5 for release-body, 15 for structured), DialogReleaseNotes has scrollable description with whitespace-pre-line and accessibility attributes, and tests updated to assert one highlight per version with merged descriptions.
Out of Scope Changes check ✅ Passed All changes are scoped to the stated objectives: highlights parsing logic, pagination handling, dialog styling, and test updates. No i18n changes, visual redesigns, or structured schema modifications beyond scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pawwork/fix-release-notes-pagination

Review rate limit: 0/10 reviews remaining, refill in 58 minutes and 6 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/app/src/components/dialog-release-notes.tsx`:
- Around line 77-79: The description paragraph in DialogReleaseNotes (the <p>
that renders {feature()?.description ?? ""}) is scrollable but not
keyboard-focusable; make it focusable by adding a ref (e.g., descriptionRef) and
a tabindex="0" plus an accessible name (aria-label or role="region" with
aria-label/aria-labelledby) so keyboard users can tab into and scroll it, and
optionally in the component's useEffect check if
descriptionRef.current.scrollHeight > descriptionRef.current.clientHeight and
call descriptionRef.current.focus() to move initial focus when content
overflows; update the element rendering the feature()?.description and the
component lifecycle logic (useEffect) accordingly.

In `@packages/app/src/context/highlights.tsx`:
- Line 12: sliceHighlights currently enforces MAX_RELEASE_HIGHLIGHTS (const
MAX_RELEASE_HIGHLIGHTS = 5) for all release shapes and thus truncates
structured-schema releases that have highlights[].items[]; change
sliceHighlights so the 5-page cap is only applied to the parsed-markdown code
path (or introduce a separate constant like MAX_PARSED_MARKDOWN_HIGHLIGHTS = 5)
and skip slicing for structured-schema releases that use nested highlights
(detect by the presence of highlights[].items or a parsed/structured flag on the
release). Update sliceHighlights to branch on the release shape (e.g.,
release.highlights.some(h => h.items) or release.isParsedMarkdown) and only
apply MAX_PARSED_MARKDOWN_HIGHLIGHTS in the parsed-markdown branch while leaving
the structured-schema branch untrimmed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4bcb63d5-87c9-4cc1-a9d2-787ef946bb42

📥 Commits

Reviewing files that changed from the base of the PR and between 7635333 and ff481ed.

📒 Files selected for processing (3)
  • packages/app/src/components/dialog-release-notes.tsx
  • packages/app/src/context/highlights.test.ts
  • packages/app/src/context/highlights.tsx

Comment thread packages/app/src/components/dialog-release-notes.tsx Outdated
Comment thread packages/app/src/context/highlights.tsx Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the release notes logic to group multiple bullet points from a single release into a single scrollable view instead of separate pages. The UI is updated to support scrolling for long descriptions, and the parsing logic now formats bullets with a '•' prefix. Feedback suggests that reducing MAX_RELEASE_HIGHLIGHTS to 5 may cause unintended truncation for structured highlights that contain many items; it is recommended to limit the number of versions displayed instead of the total number of items.

Comment thread packages/app/src/context/highlights.tsx Outdated
- Add source field to ParsedRelease ('release-body' | 'structured')
- Rename MAX_RELEASE_HIGHLIGHTS to MAX_RELEASE_VERSION_PAGES (5)
- Add MAX_STRUCTURED_HIGHLIGHTS (15) for structured schema path
- Refactor sliceHighlights to apply caps per source, not globally
- Rename parseNoticeDescriptions to parseNoticeContent
- Add keyboard accessibility to scrollable description (tabIndex, role, aria-labelledby)
- Add tests: structured 6+ items independent cap, structured 16 items limit

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/app/src/components/dialog-release-notes.tsx (1)

31-34: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset the description scroll position on page changes.

This scrollable <p> keeps its scrollTop while only the text content changes, so moving from a long note to the next page can leave the next release opened partway down. Reset the region to the top whenever index() changes so each page starts at the beginning.

Also applies to: 57-59, 79-86

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app/src/components/dialog-release-notes.tsx` around lines 31 - 34,
The scrollable description element retains its scrollTop across pages; add a ref
(e.g., descriptionRef) to the scrollable <p> and reset its scrollTop to 0
whenever the page index changes—either by calling
descriptionRef.current.scrollTop = 0 immediately after setIndex(index() + 1) in
handleNext (and similar handlers around lines 57-59 and 79-86) or, better, by
adding a useEffect that watches index() and sets
descriptionRef.current.scrollTop = 0 when index changes; reference setIndex,
index(), handleNext and the scrollable paragraph element when making the change.
packages/app/src/context/highlights.tsx (1)

109-143: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve prose when a notice mixes paragraphs and bullets.

parseNoticeContent() now only returns either summary or bullets, so any standalone text before the first list item is dropped, and trailing prose gets merged into the last bullet. A notice like Important migration note followed by bullets will silently lose that intro on the rendered version page.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/app/src/context/highlights.tsx` around lines 109 - 143,
parseNoticeContent currently discards or merges standalone prose around lists;
update parseNoticeContent to detect and preserve leading and trailing non-list
paragraphs instead of merging them into the first/last bullet. Specifically,
while iterating lines in parseNoticeContent, collect any consecutive non-list
lines before the first matched list item as an "intro" (trimNoticeItem applied),
collect list items into bullets as now, and collect any non-list lines after the
last list item as an "outro"; then return a structure that preserves all parts
(e.g., change the returned shape for mixed content to include intro?: string,
items: string[], outro?: string or a new kind "mixed") so callers can render
intro, bullet list, and outro separately; update references to
parseNoticeContent, trimNoticeItem and the result shape accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/app/src/components/dialog-release-notes.tsx`:
- Around line 31-34: The scrollable description element retains its scrollTop
across pages; add a ref (e.g., descriptionRef) to the scrollable <p> and reset
its scrollTop to 0 whenever the page index changes—either by calling
descriptionRef.current.scrollTop = 0 immediately after setIndex(index() + 1) in
handleNext (and similar handlers around lines 57-59 and 79-86) or, better, by
adding a useEffect that watches index() and sets
descriptionRef.current.scrollTop = 0 when index changes; reference setIndex,
index(), handleNext and the scrollable paragraph element when making the change.

In `@packages/app/src/context/highlights.tsx`:
- Around line 109-143: parseNoticeContent currently discards or merges
standalone prose around lists; update parseNoticeContent to detect and preserve
leading and trailing non-list paragraphs instead of merging them into the
first/last bullet. Specifically, while iterating lines in parseNoticeContent,
collect any consecutive non-list lines before the first matched list item as an
"intro" (trimNoticeItem applied), collect list items into bullets as now, and
collect any non-list lines after the last list item as an "outro"; then return a
structure that preserves all parts (e.g., change the returned shape for mixed
content to include intro?: string, items: string[], outro?: string or a new kind
"mixed") so callers can render intro, bullet list, and outro separately; update
references to parseNoticeContent, trimNoticeItem and the result shape
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d84025ea-dbfd-4dff-8491-7904d49df614

📥 Commits

Reviewing files that changed from the base of the PR and between ff481ed and e6378dc.

📒 Files selected for processing (3)
  • packages/app/src/components/dialog-release-notes.tsx
  • packages/app/src/context/highlights.test.ts
  • packages/app/src/context/highlights.tsx

- Add createEffect to reset description scrollTop on page change
- Preserve intro prose before bullets in mixed content notices
- Add intro field to ParsedNotice bullets kind
- Update formatReleaseNoticeDescription to include intro before bullets
- Add test for mixed content (intro + bullets) preservation
- Update localized update notice test to expect preserved intro

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/app/src/context/highlights.tsx`:
- Around line 114-140: The parser currently removes blank lines early which
causes paragraphs after the last list item to be folded into currentBullet;
update the splitting logic so blank lines are preserved (remove the .filter that
drops empty lines) and in the loop treat an empty trimmed line as a paragraph
break: if line.trim() === "" and currentBullet is set, push
trimNoticeItem(currentBullet), set currentBullet = undefined and hasSeenBullet =
true (skip adding to prose); keep the existing rule that prose only accumulates
when !hasSeenBullet, and continue to push any final currentBullet at the end as
before; reference symbols: notice, lines, currentBullet, hasSeenBullet,
trimNoticeItem, bullets, prose.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d1340949-49aa-4e3f-9648-2714eabc982d

📥 Commits

Reviewing files that changed from the base of the PR and between e6378dc and 5cfd746.

📒 Files selected for processing (3)
  • packages/app/src/components/dialog-release-notes.tsx
  • packages/app/src/context/highlights.test.ts
  • packages/app/src/context/highlights.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/app/src/context/highlights.test.ts

Comment thread packages/app/src/context/highlights.tsx
Astro-Han added 3 commits May 3, 2026 12:04
Ensure DOM is fully updated before resetting scroll position
when page index changes.
- Stop filtering empty lines early; use them as paragraph breaks
- Flush currentBullet when encountering an empty line
- Add test ensuring trailing prose is not folded into final bullet
@Astro-Han

Copy link
Copy Markdown
Owner Author

@gemini-code-assist Thanks for the review. This issue has been addressed in commit e6378dc.

Changes made:

  • Replaced single MAX_RELEASE_HIGHLIGHTS = 5 with two separate caps:
    • MAX_RELEASE_VERSION_PAGES = 5 — only limits release-body version pages
    • MAX_STRUCTURED_HIGHLIGHTS = 15 — preserves structured schema's original per-item cap
  • Added source: 'release-body' | 'structured' to ParsedRelease so sliceHighlights() tracks each cap independently
  • Added regression tests verifying structured highlights with 6+ items are not truncated by the 5-page release-body cap

@gemini-code-assist

Copy link
Copy Markdown

Thanks for the update, @Astro-Han. The changes to separate the pagination caps for release-body and structured highlights, along with the added regression tests, look correct and address the concerns raised. This approach ensures that the UI remains consistent while maintaining the intended data boundaries.

@Astro-Han
Astro-Han force-pushed the pawwork/fix-release-notes-pagination branch 2 times, most recently from f840915 to f095785 Compare May 3, 2026 04:39
@Astro-Han
Astro-Han merged commit da151d0 into dev May 3, 2026
47 checks passed
@Astro-Han
Astro-Han deleted the pawwork/fix-release-notes-pagination branch May 11, 2026 07:57
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.

[Task] Fix PR #372: merge release bullets per version into single toast page

1 participant