Skip to content

fix(web): give release notes their own renderer - #228

Merged
elkaix merged 1 commit into
mainfrom
fix/release-notes-design
Aug 27, 2026
Merged

fix(web): give release notes their own renderer#228
elkaix merged 1 commit into
mainfrom
fix/release-notes-design

Conversation

@elkaix

@elkaix elkaix commented Aug 27, 2026

Copy link
Copy Markdown
Member

Related Issue

No issue — follow-up to #226, which fixed the release-notes content. This fixes how that content is presented.

Problem

The update popover rendered release notes with the chat Markdown component. Three problems, in order of weight:

  1. It renders raw HTML as live DOM. Mounting markstream-vue against <img src=x onerror=...>, <script>, <b> and <i> shows <b>/<i> coming through as real elements; it strips scripts and event handlers, but it is a sanitizing HTML renderer, not a text renderer. fix(desktop): show the changelog in the update dialog #226 had to escape angle brackets in updater.ts specifically to stay inert for it.
  2. It cannot be mounted in a test — it resolves katex and mermaid workers and throws. So update-dialog.test.ts stubbed it, and this user-facing surface shipped with its rendering entirely unverified.
  3. It drags katex, mermaid and shiki into a 440px panel that shows a few bullets.

On top of that, the generated body ends with Built from <url>., which read as a stray sentence at the end of the release notes.

What changed

ReleaseNotes.vue renders the one narrow shape the release generator produces:

  • Headings, bullets and paragraphs as real elements, bound through Vue interpolation. Markup cannot be contributed by a release body no matter what upstream did to it, which retires the coupling in point 1. Escaped angle brackets are decoded back for display, so the reader sees the characters the author typed.
  • Built from <url> becomes a footnote under a rule, linking the short commit reference — and only when the URL is an actual web page, since a release body is remote input that will-navigate hands to shell.openExternal.
  • Wrapped bullets join into one item; a thematic rule that no longer separates anything is dropped.
  • The scroll region is a tab stop (role="group"): notes that outgrow the panel could not be scrolled without a pointer.
  • The fade over the last rows is conditional — it appears only when content is actually below, and clears at the end of the list. An unconditional fade claims there is more to read when there is not.
  • Overflow: min-width: 0 + overflow-wrap: anywhere + overflow-x: clip, so a long commit URL wraps instead of being silently clipped by the overflow: hidden shell.

Verified in a real browser (built bundle, faked desktop bridge): no horizontal overflow, fade on with content below and off at the end, both light and dark.

The chat renderer is untouched and still used by chat, plan, goal, approval, question and file-preview surfaces.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue — follow-up to fix(desktop): show the changelog in the update dialog #226, no separate issue.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Tests

apps/pythinker-web/test/release-notes.test.ts covers the generated body, heading grouping, wrapped bullets, provenance extraction and short-ref labelling, a rejected non-web scheme, the escaped-markup payload from #226, and the empty state.

update-dialog.test.ts no longer stubs the renderer — that is the change that makes this surface real. app-shell-contracts.test.ts now asserts the chat renderer is not imported by the sidebar.

1012 web tests, typecheck and lint all green; dist-web rebuilt and restaged in the same commit.

The update popover rendered release notes with the chat Markdown component.
That component renders raw HTML as live DOM, cannot be mounted in a test (it
resolves katex and mermaid workers, so the popover had to stub it and shipped
unverified), and pulls katex, mermaid and shiki into a 440px panel.

Release notes arrive in one narrow shape we generate ourselves, so render that
shape directly: headings, bullets and paragraphs as real elements through Vue
interpolation, which cannot contribute markup whatever upstream did to the
body. `Built from <url>` leaves the list and becomes a footnote linking the
short commit reference, and only when the URL is an actual web page.

The scroll region is now a tab stop, since notes that outgrow the panel could
not be scrolled without a pointer, and the fade over the last rows appears
only when something is actually below it.
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 102 files, which is 2 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 49fcfc5d-fb9b-4edd-95cf-68c47b32f836

📥 Commits

Reviewing files that changed from the base of the PR and between e9ebda6 and 2e31e3d.

📒 Files selected for processing (102)
  • .changeset/desktop-release-notes-design.md
  • apps/pythinker-code/dist-web/.web-bundle-manifest.json
  • apps/pythinker-code/dist-web/assets/CodeBlockNode-C2vsKEwS.js
  • apps/pythinker-code/dist-web/assets/DesignSystemView-BxjfRQFt.js
  • apps/pythinker-code/dist-web/assets/Tooltip-DKC-5Acf.js
  • apps/pythinker-code/dist-web/assets/abnfDiagram-VCTEODGH-Cfi81rrz.js
  • apps/pythinker-code/dist-web/assets/arc-Etqrqc_j.js
  • apps/pythinker-code/dist-web/assets/architectureDiagram-5GKGNRK7-B06L70xf.js
  • apps/pythinker-code/dist-web/assets/blockDiagram-NRAW4CY4-CWsb70rx.js
  • apps/pythinker-code/dist-web/assets/c4Diagram-UCG6FXSJ-BqUzJFao.js
  • apps/pythinker-code/dist-web/assets/channel-DVBIThbf.js
  • apps/pythinker-code/dist-web/assets/channel-DbAXcyo9.js
  • apps/pythinker-code/dist-web/assets/chunk-2Q5K7J3B-PPvuNV4w.js
  • apps/pythinker-code/dist-web/assets/chunk-5VM5RSS4-Bknm1p5G.js
  • apps/pythinker-code/dist-web/assets/chunk-F27PBJKO-D2T0DuV8.js
  • apps/pythinker-code/dist-web/assets/chunk-G27WJ6UU-NF4ixEX0.js
  • apps/pythinker-code/dist-web/assets/chunk-JWPE2WC7-CxLa2uLf.js
  • apps/pythinker-code/dist-web/assets/chunk-LCL6LL3I-CUZdXmGE.js
  • apps/pythinker-code/dist-web/assets/chunk-POPQ4Y6H-QWxxp__B.js
  • apps/pythinker-code/dist-web/assets/chunk-SVP7TREG-C-hHuJA3.js
  • apps/pythinker-code/dist-web/assets/chunk-XXDRQBXY-DwGxoxLw.js
  • apps/pythinker-code/dist-web/assets/classDiagram-DTDB5LWJ-gPGZrNo0.js
  • apps/pythinker-code/dist-web/assets/classDiagram-DTDB5LWJ-s3BqGYpE.js
  • apps/pythinker-code/dist-web/assets/classDiagram-v2-JRS7N3AN-gPGZrNo0.js
  • apps/pythinker-code/dist-web/assets/classDiagram-v2-JRS7N3AN-s3BqGYpE.js
  • apps/pythinker-code/dist-web/assets/cose-bilkent-JH36ORCC-CWlfC9T4.js
  • apps/pythinker-code/dist-web/assets/cssMode-DJG2qq72.js
  • apps/pythinker-code/dist-web/assets/cynefin-OW5HDTMX-2CeSmdWd.js
  • apps/pythinker-code/dist-web/assets/cynefinDiagram-5FMLGOSQ-D2TWu_mu.js
  • apps/pythinker-code/dist-web/assets/dagre-3AP2YEHR-DSflNKPk.js
  • apps/pythinker-code/dist-web/assets/diagram-S7CK7UJ4-C32K_Gra.js
  • apps/pythinker-code/dist-web/assets/diagram-UQ7AKVKN-B31XcI-8.js
  • apps/pythinker-code/dist-web/assets/diagram-VSXAHHWV-OdTjbLTS.js
  • apps/pythinker-code/dist-web/assets/diagram-VX7I27RA-BQ_qP2yt.js
  • apps/pythinker-code/dist-web/assets/diagram-Z3DM3KII-D1V-tqAN.js
  • apps/pythinker-code/dist-web/assets/ebnfDiagram-PWID7BFC-BymAi4PB.js
  • apps/pythinker-code/dist-web/assets/editor.main-Dd-S5XAV.js
  • apps/pythinker-code/dist-web/assets/erDiagram-SSCWMZ5O-CE6bwZkP.js
  • apps/pythinker-code/dist-web/assets/flowDiagram-A5DVABFB-C11wtrhX.js
  • apps/pythinker-code/dist-web/assets/freemarker2-Ck475uoi.js
  • apps/pythinker-code/dist-web/assets/ganttDiagram-EL5Y4UJY-HRV1hRO3.js
  • apps/pythinker-code/dist-web/assets/gitGraphDiagram-WWUBYQGX-DwHQZ56q.js
  • apps/pythinker-code/dist-web/assets/handlebars-BvMj2ias.js
  • apps/pythinker-code/dist-web/assets/html-5aSpGB2O.js
  • apps/pythinker-code/dist-web/assets/htmlMode-Dozkuljc.js
  • apps/pythinker-code/dist-web/assets/index-B1fV40mQ.css
  • apps/pythinker-code/dist-web/assets/index-B7-Zpeer.js
  • apps/pythinker-code/dist-web/assets/index-BO4scgvj.css
  • apps/pythinker-code/dist-web/assets/index-CWjfKvGV.js
  • apps/pythinker-code/dist-web/assets/index-_ekSCUrr.js
  • apps/pythinker-code/dist-web/assets/index-wrjrX3kT.js
  • apps/pythinker-code/dist-web/assets/index10-tLlhFh-s.js
  • apps/pythinker-code/dist-web/assets/index11-C0iw2Joc.js
  • apps/pythinker-code/dist-web/assets/index5-BtKbo-Hw.js
  • apps/pythinker-code/dist-web/assets/index6-l2x6GlUq.js
  • apps/pythinker-code/dist-web/assets/index7-CCGOVFhy.js
  • apps/pythinker-code/dist-web/assets/index8-uJh6qxuo.js
  • apps/pythinker-code/dist-web/assets/infoDiagram-RXCK75RN-DNBW7kUe.js
  • apps/pythinker-code/dist-web/assets/ishikawaDiagram-5VMMS53U-DUHSutWK.js
  • apps/pythinker-code/dist-web/assets/javascript-K-PJiAQP.js
  • apps/pythinker-code/dist-web/assets/journeyDiagram-EYS64GPL-Ds_KQ4KP.js
  • apps/pythinker-code/dist-web/assets/jsonMode-CK26G-bu.js
  • apps/pythinker-code/dist-web/assets/kanban-definition-3QL26DDD-aFY2bO7o.js
  • apps/pythinker-code/dist-web/assets/layout-BiqB16Ys.js
  • apps/pythinker-code/dist-web/assets/linear-BxkqhfY8.js
  • apps/pythinker-code/dist-web/assets/liquid-BjMVk2aE.js
  • apps/pythinker-code/dist-web/assets/lspLanguageFeatures-YGoPXRZW.js
  • apps/pythinker-code/dist-web/assets/mdx-Cl4MNLdb.js
  • apps/pythinker-code/dist-web/assets/mermaid.core-BbyALZhr.js
  • apps/pythinker-code/dist-web/assets/mindmap-definition-FBJOCRG2-Bil0kD07.js
  • apps/pythinker-code/dist-web/assets/pegDiagram-XKGWAZYB-B9bQioMK.js
  • apps/pythinker-code/dist-web/assets/pieDiagram-E7YTZNPT-C3nobKyg.js
  • apps/pythinker-code/dist-web/assets/python-1l_ieg17.js
  • apps/pythinker-code/dist-web/assets/quadrantDiagram-AXDQQJYC-MuFjZvkZ.js
  • apps/pythinker-code/dist-web/assets/railroadDiagram-O6MQD6OU-zuNqXBr9.js
  • apps/pythinker-code/dist-web/assets/razor-D78QBlcE.js
  • apps/pythinker-code/dist-web/assets/requirementDiagram-EFPCY7ZU-DoytdSbG.js
  • apps/pythinker-code/dist-web/assets/sankeyDiagram-P5KCCOFB-BMChrGKp.js
  • apps/pythinker-code/dist-web/assets/sequenceDiagram-WJ2MYXX4-BG47Sll4.js
  • apps/pythinker-code/dist-web/assets/sizeCapture-X5ZJPWSS-Ddy3oC6p.js
  • apps/pythinker-code/dist-web/assets/stateDiagram-HBIQ2CUA-BX0s-9zx.js
  • apps/pythinker-code/dist-web/assets/stateDiagram-v2-4QOOHH4V-CWoQsfyx.js
  • apps/pythinker-code/dist-web/assets/stateDiagram-v2-4QOOHH4V-ChgvKE0G.js
  • apps/pythinker-code/dist-web/assets/swimlanes-XN3QIQJK-ozD5N5vP.js
  • apps/pythinker-code/dist-web/assets/swimlanesDiagram-VK2B7HYN-ByMNMLVk.js
  • apps/pythinker-code/dist-web/assets/swimlanesDiagram-VK2B7HYN-CF8LPpri.js
  • apps/pythinker-code/dist-web/assets/timeline-definition-24CTP7MA-BbFQqPWu.js
  • apps/pythinker-code/dist-web/assets/tsMode-B_rX91Jo.js
  • apps/pythinker-code/dist-web/assets/typescript-dlCkBrVF.js
  • apps/pythinker-code/dist-web/assets/vennDiagram-4TSXK5OY-j1vOuqKZ.js
  • apps/pythinker-code/dist-web/assets/vue.runtime.esm-bundler-DpfxMEtL.js
  • apps/pythinker-code/dist-web/assets/wardleyDiagram-VM6X3IG4-BK8lMznE.js
  • apps/pythinker-code/dist-web/assets/xml-IENzgZeC.js
  • apps/pythinker-code/dist-web/assets/xychartDiagram-S5SC5T6Z-CnaBGRbi.js
  • apps/pythinker-code/dist-web/assets/yaml-CYWsYkjw.js
  • apps/pythinker-code/dist-web/index.html
  • apps/pythinker-web/src/components/ReleaseNotes.vue
  • apps/pythinker-web/src/components/Sidebar.vue
  • apps/pythinker-web/src/i18n/locales/en/update.ts
  • apps/pythinker-web/test/app-shell-contracts.test.ts
  • apps/pythinker-web/test/release-notes.test.ts
  • apps/pythinker-web/test/update-dialog.test.ts

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


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.

❤️ Share

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 27, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pymodel/pythinker-code@2e31e3d
npx https://pkg.pr.new/@pymodel/pythinker-code@2e31e3d

commit: 2e31e3d

Comment thread apps/pythinker-code/dist-web/assets/chunk-G27WJ6UU-NF4ixEX0.js
Comment thread apps/pythinker-code/dist-web/assets/chunk-G27WJ6UU-NF4ixEX0.js
@elkaix
elkaix merged commit 62e2e75 into main Aug 27, 2026
25 checks passed
@elkaix
elkaix deleted the fix/release-notes-design branch August 27, 2026 21:59
elkaix pushed a commit that referenced this pull request Aug 28, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @pymodel/pythinker-desktop@0.3.9

### Patch Changes

- [#228](#228)
[`62e2e75`](62e2e75)
Thanks [@elkaix](https://github.com/elkaix)! - Lay out the update
release notes as a readable list with the build reference as a footnote,
and show when there is more to scroll.

- [#226](#226)
[`e9ebda6`](e9ebda6)
Thanks [@elkaix](https://github.com/elkaix)! - Show the changelog for
the new version in the update dialog instead of a build stamp with raw
HTML tags.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Update dialogs now display the new version’s changelog instead of a
raw build stamp or HTML markup.
- Release notes are presented as a readable list with the build
reference shown as a footnote.
- A scroll indicator appears when additional release-note content is
available.

- **Release**
  - Desktop application updated to version 0.3.9.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant