feat(notices): add the 4.0.0 release modal

The thank-you modal was one column of markdown for every notice. A release
needs more room than that: what shipped, and why it took what it took.

Adds a `release` block to the notice contract. A notice carrying it renders
through ReleaseNoticeModal instead of the generic body — the release on the
left, the note from the maintainer on the right, both halves driven entirely
by translation keys so the next release only edits the registry entry.

Sizing lives on one CSS variable (--rn-s), so the panel scales as a piece and
the design's proportions hold at every breakpoint.

The thank-you notice gets maxVersion 4.0.0: from there on the release modal
carries the same thank-you and the same two support links, and installs still
on 3.x keep the old one.

All 23 locales.
This commit is contained in:
Maurice
2026-08-22 10:56:14 +02:00
committed by Maurice
parent 76c8c349b1
commit bb7c484ca4
31 changed files with 1914 additions and 0 deletions
@@ -9,6 +9,7 @@ import { useSystemNoticeStore } from '../../store/systemNoticeStore.js';
import type { SystemNoticeDTO } from '../../store/systemNoticeStore.js';
import { useTranslation, isRtlLanguage } from '../../i18n/index.js';
import { runNoticeAction } from './noticeActions.js';
import { ReleaseNoticeModal } from './ReleaseNoticeModal.js';
import { lockBodyScroll } from '../../utils/bodyScrollLock.js';
const ReactMarkdown = React.lazy(() =>
@@ -864,5 +865,18 @@ export function ModalRenderer({ notices }: Props) {
const S = useSystemNoticeModal(notices);
// No notice to show
if (!S.notice) return null;
// A notice carrying `release` draws its own two-column panel, but keeps this
// hook's ESC handling, scroll lock and dismissal.
if (S.notice.release && !S.isMobile) {
return (
<ReleaseNoticeModal
notice={S.notice}
visible={S.visible}
onDismiss={S.handleDismissAll}
onCTA={S.handleCTA}
onSecondaryCTA={S.handleSecondaryCTA}
/>
);
}
return S.isMobile ? <MobileNoticeSheet {...S} /> : <DesktopNoticeModal {...S} />;
}