mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 05:26:47 +00:00
81decb843d
useGithubReleases holds the paged fetch, the prerelease filter, the expanded rows and the date format, and parseReleaseNotes splits a release body into the blocks both panels draw. The phone keeps its page walk past hidden prereleases through the fillPages option, its repository link and its own inline link scan; the desktop keeps one page per load.
49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
/** 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[] };
|
|
|
|
/** Escapes the characters that would let release text break out of the HTML it is placed in. */
|
|
export function escapeReleaseHtml(str: string): string {
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* 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.
|
|
*/
|
|
export function parseReleaseNotes(body: string): ReleaseNoteBlock[] {
|
|
const blocks: ReleaseNoteBlock[] = [];
|
|
let listItems: string[] = [];
|
|
|
|
const flushList = () => {
|
|
if (listItems.length > 0) {
|
|
blocks.push({ kind: 'ul', items: listItems });
|
|
listItems = [];
|
|
}
|
|
};
|
|
|
|
for (const line of body.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed) {
|
|
flushList();
|
|
continue;
|
|
}
|
|
|
|
if (trimmed.startsWith('### ')) {
|
|
flushList();
|
|
blocks.push({ kind: 'h4', text: trimmed.slice(4) });
|
|
} else if (trimmed.startsWith('## ')) {
|
|
flushList();
|
|
blocks.push({ kind: 'h3', text: trimmed.slice(3) });
|
|
} else if (/^[-*] /.test(trimmed)) {
|
|
listItems.push(trimmed.slice(2));
|
|
} else {
|
|
flushList();
|
|
blocks.push({ kind: 'p', text: trimmed });
|
|
}
|
|
}
|
|
flushList();
|
|
return blocks;
|
|
}
|