From ab3268896d2387a1dba36df2881a10a2ebf8dce6 Mon Sep 17 00:00:00 2001 From: Ryan Vogel Date: Sun, 25 Jan 2026 20:56:37 -0500 Subject: [PATCH] Add highlight tag parsing for changelog with video support --- .../console/app/src/routes/changelog.json.ts | 110 ++++++++++++++++++ .../app/src/routes/changelog/index.css | 35 ++++++ .../app/src/routes/changelog/index.tsx | 83 ++++++++++++- 3 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 packages/console/app/src/routes/changelog.json.ts diff --git a/packages/console/app/src/routes/changelog.json.ts b/packages/console/app/src/routes/changelog.json.ts new file mode 100644 index 000000000..b668229f8 --- /dev/null +++ b/packages/console/app/src/routes/changelog.json.ts @@ -0,0 +1,110 @@ +type Release = { + tag_name: string + name: string + body: string + published_at: string + html_url: string +} + +type Highlight = { + source: string + title: string + description: string + shortDescription?: string + image?: { + src: string + width: string + height: string + } + video?: string +} + +function parseHighlights(body: string): Highlight[] { + const highlights: Highlight[] = [] + const regex = /([\s\S]*?)<\/highlight>/g + let match + + while ((match = regex.exec(body)) !== null) { + const source = match[1] + const content = match[2] + + const titleMatch = content.match(/

([^<]+)<\/h2>/) + const pMatch = content.match(/([^<]+)<\/p>/) + const imgMatch = content.match(/ { + const parsed = parseMarkdown(release.body || "") + return { + tag: release.tag_name, + name: release.name, + date: release.published_at, + url: release.html_url, + highlights: parsed.highlights, + sections: parsed.sections, + } + }), + } +} diff --git a/packages/console/app/src/routes/changelog/index.css b/packages/console/app/src/routes/changelog/index.css index a445c7447..a06fb0055 100644 --- a/packages/console/app/src/routes/changelog/index.css +++ b/packages/console/app/src/routes/changelog/index.css @@ -465,6 +465,41 @@ } } } + + [data-component="highlights"] { + display: flex; + flex-direction: column; + gap: 2rem; + margin-bottom: 1.5rem; + } + + [data-component="highlight"] { + h4 { + font-size: 14px; + font-weight: 600; + color: var(--color-text-strong); + margin-bottom: 8px; + } + + p[data-slot="title"] { + font-weight: 500; + color: var(--color-text-strong); + margin-bottom: 4px; + } + + p { + color: var(--color-text); + line-height: 1.5; + margin-bottom: 12px; + } + + img, + video { + max-width: 100%; + height: auto; + border-radius: 4px; + } + } } a { diff --git a/packages/console/app/src/routes/changelog/index.tsx b/packages/console/app/src/routes/changelog/index.tsx index c1b931fe3..34fd5f83b 100644 --- a/packages/console/app/src/routes/changelog/index.tsx +++ b/packages/console/app/src/routes/changelog/index.tsx @@ -40,6 +40,59 @@ function formatDate(dateString: string) { }) } +type Highlight = { + source: string + title: string + description: string + shortDescription?: string + image?: { + src: string + width: string + height: string + } + video?: string +} + +function parseHighlights(body: string): Highlight[] { + const highlights: Highlight[] = [] + const regex = /([\s\S]*?)<\/highlight>/g + let match + + while ((match = regex.exec(body)) !== null) { + const source = match[1] + const content = match[2] + + const titleMatch = content.match(/

([^<]+)<\/h2>/) + const pMatch = content.match(/([^<]+)<\/p>/) + const imgMatch = content.match(/ +

{props.highlight.source}

+

{props.highlight.title}

+

{props.highlight.description}

+ + + + {props.highlight.title} + + + ) +} + export default function Changelog() { const releases = createAsync(() => getReleases()) @@ -120,6 +196,11 @@ export default function Changelog() {
+ 0}> +
+ {(highlight) => } +
+
{(section) => (