Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
534a1a2
feat: integration download page with real data
julhoang May 21, 2026
1d038d5
style: improve release card styling
julhoang May 21, 2026
16f902e
feat: feed dependencies stats into LLM prompt for whats new section
julhoang May 25, 2026
70c4a3f
feat: allow inline code in whats new section content
julhoang May 25, 2026
0df8655
feat: add empty state design for whats new card
julhoang May 25, 2026
f3d14b1
feat: add release report button to downloads page
julhoang May 26, 2026
0337d56
feat: add rss button to downloads page
julhoang May 26, 2026
5c3b9ae
feat: add version banner to downloads page
julhoang May 26, 2026
8427e9e
refactor: move version message construction from template to view and…
julhoang May 26, 2026
8a18c14
chore: code clean up and add tests
julhoang May 26, 2026
33d0aed
chore: minor improvement to AI content formatting
julhoang May 26, 2026
77ef9b1
fix: only show whats new content when it has been approved and add test
julhoang May 26, 2026
afd7fa3
style: UX improvement for the copy button in downloads table
julhoang May 26, 2026
b5a1cdb
style: hide whats new card on development pages (master and develop)
julhoang May 26, 2026
759ee22
chore: reformat files
julhoang May 26, 2026
94840f0
chore: use to_v3_profile_dict in get_v3_contributors
julhoang May 26, 2026
60727f5
chore: update script so that it grabs release note from S3 bucket
julhoang May 26, 2026
80e4979
refactor: convert the seed release note script to django manage command
julhoang May 29, 2026
bb61471
feat: use bleach to enforce allowed tags
julhoang May 29, 2026
90d7d16
refactor: use ENUM for release page heading
julhoang May 29, 2026
69dc3fe
refactor: move the release report button from hero into whats new card
julhoang Jun 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions libraries/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.db.models import Count, Exists, OuterRef
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.html import format_html

from core.models import RenderedContent
from libraries.constants import (
Expand Down Expand Up @@ -91,8 +92,61 @@ def get_context_data(self, **kwargs):
)

context["version_alert"] = alert_visible
# V3-only: legacy templates build their own alert markup and never read
# version_alert_message, so don't compute it off the v3 render path.
if alert_visible and getattr(self, "_v3_active", False):
context["version_alert_message"] = self.get_version_alert_message(context)
return context

def get_version_alert_message(self, context):
"""Build the v3 version-alert banner message (rendered by
v3/includes/_version_alert.html).

Returns None when the inputs needed to phrase the alert are missing —
e.g. doc pages that set version_alert but never render the v3 banner.
"""
selected = context.get("selected_version")
current = context.get("current_version")
url = context.get("version_alert_url")
if not (selected and current and url):
return None

if selected == current:
return format_html(
"You've currently chosen the {name} version. If a newer "
"release comes out, you will continue to view the {name} "
'version, not the new <a href="{url}">latest release</a>.',
name=current.display_name,
url=url,
)

current_link = format_html(
' The <a href="{url}">current version</a> is {name}.',
url=url,
name=current.display_name,
)

if selected.beta:
return format_html(
"This is a beta version of Boost.{link}", link=current_link
)

if selected.full_release:
year = selected.release_date.year if selected.release_date else ""
return format_html(
"This is an older version of Boost and was released in "
"{year}.{link}",
year=year,
link=current_link,
)

return format_html(
"This version of Boost is under active development. You are "
"currently in the {name} branch.{link}",
name=selected.display_name,
link=current_link,
)


class BoostVersionMixin:
def dispatch(self, request, *args, **kwargs):
Expand Down
60 changes: 60 additions & 0 deletions libraries/tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,63 @@ def test_version_alert_mixin(version):
assert context["version"] == old_version
assert context["latest_version"] == latest_version
assert context["version_alert"]


def _alert_message(selected, current, url="/releases/latest/"):
return VersionAlertMixin().get_version_alert_message(
{
"selected_version": selected,
"current_version": current,
"version_alert_url": url,
}
)


def test_version_alert_message_none_when_inputs_missing():
from versions.models import Version

assert VersionAlertMixin().get_version_alert_message({}) is None
assert _alert_message(Version(name="boost-1.90.0"), None) is None


def test_version_alert_message_sticky_latest():
from versions.models import Version

current = Version(name="boost-1.90.0", full_release=True)
msg = _alert_message(current, current)
assert "you will continue to view" in msg
assert 'href="/releases/latest/"' in msg


def test_version_alert_message_beta():
from versions.models import Version

current = Version(name="boost-1.90.0", full_release=True)
selected = Version(name="boost-1.91.0-beta", beta=True, full_release=False)
msg = _alert_message(selected, current)
assert "beta version of Boost" in msg
assert "current version" in msg


def test_version_alert_message_older_release():
from versions.models import Version

current = Version(name="boost-1.90.0", full_release=True)
selected = Version(
name="boost-1.70.0",
full_release=True,
release_date=datetime.date(2018, 1, 1),
)
msg = _alert_message(selected, current)
assert "older version of Boost" in msg
assert "2018" in msg


def test_version_alert_message_dev_branch():
from versions.models import Version

current = Version(name="boost-1.90.0", full_release=True)
selected = Version(name="develop", beta=False, full_release=False)
msg = _alert_message(selected, current)
assert "under active development" in msg
assert "develop" in msg
46 changes: 34 additions & 12 deletions static/css/v3/banner.css
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
.banner {
width: 100%;
display: flex;
padding: var(--space-default, 8px) var(--space-large, 16px);
justify-content: center;
padding: var(--space-default);
align-items: center;
gap: var(--space-large, 16px);
gap: var(--space-large);
border-radius: var(--border-radius-l);
background: var(--color-surface-brand-accent-default);
}

border-radius: var(--border-radius-l, 8px);
background: var(--color-surface-brand-accent-default, #FFA000);
.banner__body {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-large);
min-width: 0;
}

.banner__message {
color: var(--color-text-on-accent, #050816);

/* Sans/Desktop/Regular/XS/Tight */
color: var(--color-text-on-accent);
font-family: var(--font-sans);
font-size: var(--font-size-xs, 12px);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-tight);
/* 12px */
letter-spacing: -0.12px;
letter-spacing: var(--letter-spacing-tight);
}

.banner__message a {
Expand All @@ -30,10 +34,28 @@
text-underline-offset: auto;
}

.banner__icon {
.banner__icon, .banner__close-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
aspect-ratio: 1/1;
fill: var(--color-text-on-accent);
}

.banner__close {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-xs);
margin: calc(-1 * var(--space-xs)) 0;
background: transparent;
border: none;
cursor: pointer;
color: var(--color-text-on-accent);
border-radius: var(--border-radius-s);
}

.banner__close:hover {
opacity: 0.7;
}
19 changes: 19 additions & 0 deletions static/css/v3/downloads-table-card.css
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,25 @@
color: var(--color-text-error);
}

.downloads-table-card__copy-icon {
display: inline-flex;
align-items: center;
justify-content: center;
pointer-events: none;
}

.downloads-table-card__copy-icon--check {
display: none;
}

.downloads-table-card__copy-btn.is-copied .downloads-table-card__copy-icon--copy {
display: none;
}

.downloads-table-card__copy-btn.is-copied .downloads-table-card__copy-icon--check {
display: inline-flex;
}

.js-copy-enabled .downloads-table-card__copy-btn {
display: flex;
}
Expand Down
30 changes: 25 additions & 5 deletions static/css/v3/heros.css
Original file line number Diff line number Diff line change
Expand Up @@ -368,16 +368,29 @@
width: 100%;
max-width: 1440px;
display: flex;
align-items: center;
justify-content: space-between;
flex-direction: column;
gap: var(--space-large);
padding-bottom: var(--space-avatar);
padding-top: var(--space-hero-padding-top);
}

.hero-library--no-image .hero-library__block {
.hero-library__main {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}

.hero-library--no-image .hero-library__main {
align-items: center;
}

/* Banner sits inside __block; inset its sides to match content/image margins */
.hero-library__block > .banner {
margin: 0 var(--space-large);
width: auto;
}

.hero-library__content {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -516,17 +529,24 @@
@media (max-width: 767px) {

.hero-library__block {
padding: 96px var(--space-default) 32px var(--space-default);
}

.hero-library__main {
min-height: 210px;
flex-direction: column;
justify-content: center;
padding: 96px var(--space-default) 32px var(--space-default);
}

.hero-library--no-image .hero-library__block {
.hero-library--no-image .hero-library__main {
min-height: auto;
height: auto;
}

.hero-library__block > .banner {
margin: 0;
}

.hero-library {
background-size: cover;
background-position: center top;
Expand Down
54 changes: 48 additions & 6 deletions static/css/v3/markdown-card.css
Original file line number Diff line number Diff line change
@@ -1,24 +1,55 @@
.card__column.markdown-content {
gap: var(--space-medium);
line-height: var(--line-height-code);
}

.markdown-content ul {
list-style: inside;
.markdown-content ul,
.markdown-content ol {
list-style: none;
padding: 0;
}

.markdown-content li {
position: relative;
padding-left: var(--space-xlarge);
margin-top: var(--space-medium);

}

.markdown-content ul > li::before {
content: "\2022";
position: absolute;
left: 0;
top: 0;
width: var(--space-xlarge);
height: calc(var(--font-size-small) * 1.2);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-primary);
}

.markdown-content a {
color: var(--color-text-link-accent);
text-decoration: underline;
text-decoration-skip-ink: none;
text-underline-position: from-font;
}

.markdown-content {
color: var(--color-text-secondary);
padding: 0 var(--space-large);
font-size: var(--font-size-small);
font-weight: var(--font-weight-regular);
line-height: var(--line-height-default);
/* 16.8px */
letter-spacing: var(--letter-spacing-tight);
overflow-y: auto;
overflow-wrap: anywhere;
min-width: 0;
}

.markdown-content pre {
white-space: pre-wrap;
overflow-wrap: anywhere;
}

.markdown-content h1,
Expand All @@ -30,8 +61,7 @@
color: var(--color-text-primary);
font-family: var(--font-display);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-tight);
margin: var(--space-default) 0 0;
margin: 0;
}

.markdown-content h1 { font-size: var(--font-size-large); }
Expand All @@ -57,3 +87,15 @@
list-style-position: outside;
padding-left: 14px;
}

.markdown-content :is(code, tt, pre) {
display: inline;
font-family: var(--font-code);
font-size: 0.88em;
font-weight: var(--font-weight-medium);
color: var(--color-text-primary);
background: transparent;
padding: 0;
border: none;
border-radius: 0;
}
Loading
Loading