Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
110 changes: 91 additions & 19 deletions ak/homepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,11 @@
from core.constants import SLACK_MEMBER_COUNT
from core.models import HomepageSettings
from core.templatetags.custom_static import large_static
from libraries.models import LibraryVersion, Tier
from libraries.utils import build_library_intro_context
from libraries.models import FEATURED_LIBRARY_TIERS, Library, LibraryVersion
from libraries.utils import build_library_intro_context, get_documentation_url
from news.models import Entry
from versions.models import Version

# Fallback code snippet shown on the Get Started card when no featured
# library snippet is available. TODO: replace with a proper empty state.
HELLO_WORLD_SNIPPET = """#include <iostream>
int main()
{
std::cout << "Hello, Boost.";
}"""

# Hero illustration for the V3 homepage.
HERO_LEGACY_IMAGE_URL_LIGHT = large_static("img/v3/home-page/heros.png")
HERO_LEGACY_IMAGE_URL_DARK = large_static("img/v3/home-page/heros_light.png")
Expand Down Expand Up @@ -45,7 +37,7 @@ def get_v3_featured_library():
return (
LibraryVersion.objects.filter(
version=latest_version,
library__tier__in=[Tier.FLAGSHIP, Tier.CORE],
library__tier__in=FEATURED_LIBRARY_TIERS,
)
.order_by("?")
.first()
Expand Down Expand Up @@ -152,17 +144,97 @@ def build_join_developers_links():
]


def build_get_started_code():
"""The Get Started code card. Hardcoded for now; a redesign of this
component is tracked in a separate PR.
def build_library_highlight_carousel(limit=3):
"""Per-slide content for the homepage 'Explore battle tested libraries'
carousel.

Shows the libraries an editor curated in HomepageSettings; when fewer than
`limit` are curated, the remaining slots are filled with random flagship/core
libraries in the latest release (admins may curate more than `limit`). When
admins curate libraries, their pinned order is preserved (fallback top-ups
follow); with no pins, slides are ordered by description length for UI
appearance. Returns a list of dicts (name, category_tags, description,
added_in_version, docs_url); empty when no candidate library exists.
"""
return {
"heading": "Get started with our libraries",
"code": HELLO_WORLD_SNIPPET,
"language": "cpp",
"library_slug": "",
latest = Version.objects.most_recent()
highlighted_ids = list(
HomepageSettings.load().highlighted_libraries.values_list("id", flat=True)
)
library_ids = list(highlighted_ids)

# Admins may curate more than `limit`; only top up when they curate fewer.
remaining_slots = limit - len(highlighted_ids)
if remaining_slots > 0:
library_ids += list(
Library.objects.filter(
tier__in=FEATURED_LIBRARY_TIERS,
library_version__version=latest,
)
.exclude(id__in=highlighted_ids)
.distinct()
.order_by("?")
.values_list("id", flat=True)[:remaining_slots]
)

libraries = Library.objects.filter(id__in=library_ids).prefetch_related(
"categories"
)
if not libraries:
return []

library_versions = {
lv.library_id: lv
for lv in LibraryVersion.objects.filter(library__in=libraries, version=latest)
}

category_list_url = reverse(
"libraries-list",
kwargs={"version_slug": "latest", "library_view_str": "list"},
)

slides_by_id = {}
for library in libraries:
lv = library_versions.get(library.id)
first_version = library.first_boost_version
# Fall back to the library detail page when the latest release has no
# documentation URL, so the CTA always points somewhere useful.
docs_url = get_documentation_url(lv, latest=True) if lv else ""
if not docs_url:
docs_url = reverse(
"library-detail",
kwargs={"version_slug": "latest", "library_slug": library.slug},
)
slides_by_id[library.id] = {
"name": library.name,
"category_tags": [
(
{
**tag,
"url": f"{category_list_url}?category={tag['slug']}",
"aria_label": f"Browse {tag['label']} libraries",
}
if tag["slug"]
else tag
)
for tag in library.category_tags
],
"description": (
(lv.description if lv else None) or library.description or ""
),
"added_in_version": (first_version.display_name if first_version else ""),
"docs_url": docs_url,
}

if highlighted_ids:
# Respect the admin's pinned order; fallback top-ups follow.
return [slides_by_id[lib_id] for lib_id in library_ids]
# No pins: order by description length, then category count, for looks.
return sorted(
slides_by_id.values(),
key=lambda slide: (len(slide["description"]), len(slide["category_tags"])),
reverse=True,
)


def build_library_intro():
"""Library Intro card content for the V3 homepage, or None if no
Expand Down
6 changes: 3 additions & 3 deletions ak/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
from ak.homepage import (
WHY_BOOST_CARDS,
build_community_posts,
build_get_started_code,
build_join_developers_links,
build_library_highlight_carousel,
build_library_intro,
hero_image_context,
)
Expand Down Expand Up @@ -116,8 +116,8 @@ def get_v3_context_data(self, **kwargs):
# Testimonial Card
ctx["testimonial_cards"] = get_testimonial_cards(limit=5)

# Get Started Card
ctx["get_started_code"] = build_get_started_code()
# Library Highlight Carousel ("Explore battle tested libraries")
ctx["library_highlight_carousel"] = build_library_highlight_carousel()

# Library Intro Card
ctx["library_intro"] = build_library_intro()
Expand Down
24 changes: 24 additions & 0 deletions core/migrations/0009_homepagesettings_highlighted_libraries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 6.0.2 on 2026-06-30 16:03

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("core", "0008_popularsearchterm_popularsearchtermexclusion"),
("libraries", "0041_category_short_description"),
]

operations = [
migrations.AddField(
model_name="homepagesettings",
name="highlighted_libraries",
field=models.ManyToManyField(
blank=True,
help_text="Libraries shown in the homepage 'Explore battle tested libraries' carousel. Only flagship and core libraries present in the latest stable release are listed. If empty, a random selection of flagship or core libraries is used.",
related_name="+",
to="libraries.library",
),
),
]
29 changes: 23 additions & 6 deletions core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,20 @@ def wordcloud_ignore_set(self):


class HomepageSettingsForm(WagtailAdminModelForm):
"""Scopes the featured-library chooser to flagship and core libraries
present in the latest stable release, A→Z."""
"""Scopes the library choosers to flagship and core libraries present in
the latest stable release, A→Z."""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
from libraries.models import Library, Tier
from libraries.models import FEATURED_LIBRARY_TIERS, Library

qs = Library.objects.filter(tier__in=[Tier.FLAGSHIP, Tier.CORE])
qs = Library.objects.filter(tier__in=FEATURED_LIBRARY_TIERS)
latest = Version.objects.most_recent()
if latest:
qs = qs.filter(library_version__version=latest)
self.fields["featured_library"].queryset = qs.order_by(Lower("name"))
qs = qs.order_by(Lower("name"))
self.fields["featured_library"].queryset = qs
self.fields["highlighted_libraries"].queryset = qs
Comment on lines +154 to +160

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)models\.py$|carousel|library|homepage' | sed -n '1,120p'

echo
echo "== find core/models.py =="
fd -a 'models.py' . | sed 's#^\./##' | rg 'core/models.py|core'

echo
echo "== outline core/models.py =="
ast-grep outline core/models.py --view expanded | sed -n '1,220p'

echo
echo "== relevant core/models.py snippet =="
sed -n '1,230p' core/models.py

Repository: boostorg/website-v2

Length of output: 14477


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate carousel/hook builders =="
rg -n "highlighted_libraries|featured_library|LibraryHighlight|Carousel|most_recent|LibraryVersion" --glob '*.py' --glob '*.html' . | sed -n '1,220p'

echo
echo "== relevant versions/models.py =="
ast-grep outline versions/models.py --view expanded | sed -n '1,220p' || true
sed -n '1,220p' versions/models.py

echo
echo "== ak/models.py =="
sed -n '1,260p' ak/models.py

Repository: boostorg/website-v2

Length of output: 31226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Behavioral check from source text only: determine queryset semantics and whether
# selected IDs can persist outside the queryset when latest changes.
python3 - <<'PY'
from pathlib import Path
import re

for path in ["core/models.py", "ak/models.py", "templates/v3/includes/_library_highlight_carousel.html"]:
    p = Path(path)
    if not p.exists():
        print(f"\n== {path} not found ==")
        continue
    text = p.read_text()
    print(f"\n== {path} ==")
    if path.endswith("core/models.py"):
        m = re.search(r"Latest.*?\n.*?VERSION.*?\n.*?FEATURED_LIBRARY_TIERS.*?\n.*?LibraryVersion", text, re.S)
        print(m.group(0)[:2500] if m else "no Version/LibraryVersion definitions")
        idx = text.find('class HomepageSettingsForm')
        print(text[idx:idx+2200])
    elif path.endswith("ak/models.py"):
        print(text[:5500])
    elif path.endswith(".html"):
        print(text)
PY

Repository: boostorg/website-v2

Length of output: 10171


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ak/homepage.py build functions =="
sed -n '1,280p' ak/homepage.py

echo
echo "== libraries/models.py LibraryVersion definitions =="
sed -n '450,560p' libraries/models.py

echo
echo "== core/tests for homepage settings if present =="
fd -a 'test.*\.py' core | xargs -r cat -n | sed -n '1,260p' || true

echo
echo "== relevant migrations for homepage settings =="
sed -n '1,220p' core/migrations/0007_homepagesettings.py
sed -n '1,220p' core/migrations/0009_homepagesettings_highlighted_libraries.py

Repository: boostorg/website-v2

Length of output: 25228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

src = Path("ak/homepage.py").read_text()
sections = {
    "get_v3_featured_library": re.search(r"def get_v3_featured_library\(.*?^def ", src, re.S | re.M),
    "v3_highlighted_library_context": re.search(r"def v3_highlighted_library_context\(.*?^def ", src, re.S | re.M),
}
for name, m in sections.items():
    print(f"\n== {name} ==")
    if m:
        print(m.group(0))
    else:
        print("not found")

print("\n== model schema indicators ==")
for path in ["core/models.py", "core/migrations/0007_homepagesettings.py", "core/migrations/0009_homepagesettings_highlighted_libraries.py", "libraries/models.py"]:
    p = Path(path)
    if not p.exists():
        print(f"\n== {path} missing ==")
        continue
    t = p.read_text()
    if path.endswith("models.py"):
        t = "\n".join([line for line in t.splitlines() if not line.strip().startswith("#") or "Tier" in line or "LibraryVersion" in line])
    print(f"\n== {path} ==")
    for needle in ["featured_library = models", "highlighted_libraries = models", "ManyToManyField", "unique_together", "UniqueConstraint", "fields = ["]:
        idx = t.find(needle)
        if idx != -1:
            print(t[max(0, idx-200):idx+800])
PY

Repository: boostorg/website-v2

Length of output: 9783


Preserve stale homepage selections in the queryset.

HomepageSettingsForm narrows both chooser querysets to library_version__version=latest; if FeaturedLibrary or highlighted_libraries already references a library that is missing from the latest release, it disappears from the form and can’t be edited back. Include currently selected IDs in both querysets using .distinct() so existing selections remain editable while future saves still require a current LibraryVersion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/models.py` around lines 154 - 160, Update HomepageSettingsForm’s
featured_library and highlighted_libraries queryset construction to include
libraries from the latest release plus the IDs currently selected by the
instance, preserving stale selections for editing. Apply distinct() to both
querysets, while retaining validation on save so newly selected libraries still
require a current LibraryVersion.



@register_setting
Expand All @@ -178,7 +180,22 @@ class HomepageSettings(BaseGenericSetting):
),
)

panels = [FieldPanel("featured_library")]
highlighted_libraries = models.ManyToManyField(
"libraries.Library",
blank=True,
related_name="+",
help_text=(
"Libraries shown in the homepage 'Explore battle tested "
"libraries' carousel. Only flagship and core libraries present in "
"the latest stable release are listed. If empty, a random "
"selection of flagship or core libraries is used."
),
)

panels = [
FieldPanel("featured_library"),
FieldPanel("highlighted_libraries"),
]

class Meta:
verbose_name = "Homepage Settings"
Expand Down
41 changes: 41 additions & 0 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1852,6 +1852,47 @@ def get_context_data(self, **kwargs):
},
]

context["demo_library_highlight_slides"] = [
{
"name": "Beast",
"category_tags": [
{"label": "Concurrent", "slug": "concurrent"},
{"label": "IO", "slug": "io"},
],
"description": (
"Portable HTTP, WebSocket, and network operations using only "
"C++11 and Boost.Asio. Designed for performance at scale."
),
"added_in_version": "1.66.0",
"docs_url": "#",
},
{
"name": "Asio",
"category_tags": [
{"label": "Concurrent", "slug": "concurrent"},
{"label": "IO", "slug": "io"},
],
"description": (
"Cross-platform C++ library for network and low-level I/O "
"programming with a consistent asynchronous model."
),
"added_in_version": "1.35.0",
"docs_url": "#",
},
{
"name": "Hana",
"category_tags": [
{"label": "Metaprogramming", "slug": "metaprogramming"},
],
"description": (
"A modern C++ metaprogramming library for computations on both "
"types and values."
),
"added_in_version": "1.61.0",
"docs_url": "#",
},
]

context["learn_card_data"] = {
"title": "I want to learn:",
"text": "How to install Boost, use its libraries, build projects, and get help when you need it.",
Expand Down
13 changes: 8 additions & 5 deletions libraries/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ class Tier(models.IntegerChoices):
LEGACY = 40, "Legacy"


# Tiers eligible to be featured on the V3 homepage (spotlight + highlight carousel).
FEATURED_LIBRARY_TIERS = [Tier.FLAGSHIP, Tier.CORE]


class Library(models.Model):
"""
Model to represent component Libraries of Boost
Expand Down Expand Up @@ -442,13 +446,12 @@ def github_properties(self):
@cached_property
def first_boost_version(self):
"""Returns the first Boost version that included this library"""
if not self.library_version.exists():
return
return (
self.library_version.order_by("version__release_date", "version__name")
first = (
self.library_version.select_related("version")
.order_by("version__release_date", "version__name")
.first()
.version
)
return first.version if first else None

@cached_property
def github_owner(self):
Expand Down
1 change: 1 addition & 0 deletions static/css/v3/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
@import "./avatar.css";
@import "./carousel-buttons.css";
@import "./cards-carousel.css";
@import "./library-highlight-carousel.css";
@import "./badge.css";
@import "./count-badge.css";
@import "./v3-examples-section.css";
Expand Down
Loading
Loading