Compare commits

...

1 Commits

Author SHA1 Message Date
Maurice 0eb5292ca4 feat(admin): release notes show a #### heading as the feature it names
The release notes in Admin read ## as the version and ### as a section,
and printed a #### line as plain text. A release that heads each feature
inside a section with #### (Tours, TREK Studio under Added) now shows it
as a feature heading between the section and its text, on desktop and on
the phone.
2026-10-24 17:31:49 +02:00
4 changed files with 23 additions and 6 deletions
@@ -102,6 +102,13 @@ export default function GitHubPanel({ isPrerelease = false }: { isPrerelease?: b
</ul>
);
}
if (block.kind === 'h5') {
return (
<h5 key={idx} className="mb-1 mt-3 font-semibold text-content first:mt-0" style={fs(12.5, 'body')}>
{block.text}
</h5>
);
}
if (block.kind === 'h4') {
return (
<h4 key={idx} className="mb-1 mt-3 font-geist font-bold uppercase tracking-[.08em] text-content-faint first:mt-0" style={fs(10)}>
@@ -83,6 +83,13 @@ export default function MAdminGitHubPanel({ isPrerelease = false }: { isPrerelea
</ul>
)
}
if (block.kind === 'h5') {
return (
<h5 key={idx} className="text-caption font-semibold mt-3 mb-1 text-m-ink">
{block.text}
</h5>
);
}
if (block.kind === 'h4') {
return (
<h4 key={idx} className="text-xs font-semibold mt-3 mb-1 text-m-ink">
+2 -2
View File
@@ -22,11 +22,11 @@ describe('parseReleaseNotes', () => {
]);
});
it('FE-UTIL-RELNOTES-003: lines are trimmed and markers need their space', () => {
it('FE-UTIL-RELNOTES-003: lines are trimmed, markers need their space and #### heads a feature', () => {
expect(parseReleaseNotes(' ## Spaced \n-no space\n#### deep\n\n\n')).toEqual([
{ kind: 'h3', text: 'Spaced' },
{ kind: 'p', text: '-no space' },
{ kind: 'p', text: '#### deep' },
{ kind: 'h5', text: 'deep' },
]);
});
+7 -4
View File
@@ -1,5 +1,5 @@
/** One block of a release body: a heading, a paragraph, or a run of list items. */
export type ReleaseNoteBlock = { kind: 'h3' | 'h4' | 'p'; text: string } | { kind: 'ul'; items: string[] };
export type ReleaseNoteBlock = { kind: 'h3' | 'h4' | 'h5' | 'p'; text: string } | { kind: 'ul'; items: string[] };
/** Escapes the characters that would let release text break out of the HTML it is placed in. */
export function escapeReleaseHtml(str: string): string {
@@ -7,8 +7,8 @@ export function escapeReleaseHtml(str: string): string {
}
/**
* Splits a GitHub release body into the blocks both admin shells render: `### ` and `## `
* headings, `- ` or `* ` list items gathered into one list until anything else breaks the
* Splits a GitHub release body into the blocks both admin shells render: `## `, `### ` and
* `#### ` headings (the version, a section such as Added, a feature inside it), `- ` or `* ` list items gathered into one list until anything else breaks the
* run, and every other non-empty line as a paragraph. Blank lines only end a list. The
* text stays raw; each shell formats it inline itself.
*/
@@ -30,7 +30,10 @@ export function parseReleaseNotes(body: string): ReleaseNoteBlock[] {
continue;
}
if (trimmed.startsWith('### ')) {
if (trimmed.startsWith('#### ')) {
flushList();
blocks.push({ kind: 'h5', text: trimmed.slice(5) });
} else if (trimmed.startsWith('### ')) {
flushList();
blocks.push({ kind: 'h4', text: trimmed.slice(4) });
} else if (trimmed.startsWith('## ')) {