diff --git a/_extensions/gallery.py b/_extensions/gallery.py index d27f2c386..081e83907 100644 --- a/_extensions/gallery.py +++ b/_extensions/gallery.py @@ -1,10 +1,11 @@ import os -import yaml import glob from pathlib import Path import sphinx.util import re +from collections import Counter + from dodo import CATNAME_TO_CAT_MAP, CAT_TO_CATNAME_MAP logger = sphinx.util.logging.getLogger('category-gallery-extension') @@ -12,7 +13,6 @@ DEFAULT_GALLERY_CONF = { 'default_extensions': ['*.ipynb'], 'examples_dir': os.path.join('..', 'examples'), - 'labels_dir': 'labels', 'github_project': None, 'intro': 'Sample intro', 'title': 'A sample gallery title', @@ -75,29 +75,10 @@ def clean_category_name(category_name): # remove any emoji's and or leading whitespace return re.sub(r'[^a-zA-Z0-9\s]', '', category_name).strip().lower().replace(" ", "_") -def get_labels_path(app): - doc_dir = app.builder.srcdir - gallery_conf = app.config.gallery_conf - if not '_static' in app.config.html_static_path: - raise FileNotFoundError( - 'Gallery expects `html_static_path` to contain a "doc/_static/" ' - 'folder, in which the labels will be looked up.' - ) - static_dir = os.path.join(doc_dir, '_static') - labels_dir = gallery_conf['labels_dir'] - return os.path.join(static_dir, labels_dir) - -def generate_labels_rst(labels_path, labels): - labels_str = '' +def generate_labels_rst(labels): + labels_str = ' .. container:: hv-gallery-badges \n\n' for label in labels: - label_svg = os.path.join(labels_path, f'{label}.svg') - if not os.path.exists(label_svg): - raise FileNotFoundError( - f'Label {label!r} must have an SVG file in {labels_path}' - ) - # Prepend / to make it an "absolute" path from the root folder. - label_svg = '/' + label_svg - labels_str += ' ' * 8 + f'.. image:: {label_svg}\n' + labels_str += ' ' * 11 + f':bdg-primary-line:`{label}`\n' return labels_str def generate_last_updated_rst(last_updated): @@ -109,7 +90,7 @@ def generate_last_updated_rst(last_updated): """ return '' -def generate_card_grid(app, rst, projects, labels_path): +def generate_card_grid(app, rst, projects): rst += '\n.. grid:: 2 2 4 4\n :gutter: 3\n :margin: 0\n' toctree_entries=[] for section in projects: @@ -141,7 +122,7 @@ def generate_card_grid(app, rst, projects, labels_path): logger.warning(f"Thumbnail not found for {project_path}, skipping.") continue # Skip if thumbnail doesn't exist - labels_str = generate_labels_rst(labels_path, section['labels']) + labels_str = generate_labels_rst(section['labels']) last_updated_str = generate_last_updated_rst(section['last_updated']) @@ -168,7 +149,6 @@ def generate_toctree(entries, hidden=True): def generate_galleries(app): gallery_conf = app.config.gallery_conf - labels_path = get_labels_path(app) # Create category pages category_projects = {} for section in gallery_conf['sections']: @@ -182,12 +162,12 @@ def generate_galleries(app): for category in CATNAME_TO_CAT_MAP: projects = category_projects.get(category, []) - generate_category_page(app, category, projects, labels_path) + generate_category_page(app, category, projects) # Create main index.rst for gallery - generate_gallery_index(app, category_projects, labels_path) + generate_gallery_index(app, category_projects) -def generate_category_page(app, category, projects, labels_path): +def generate_category_page(app, category, projects): # Main Header rst = category + '\n' + '_'*len(category)*3 + '\n' @@ -201,7 +181,7 @@ def generate_category_page(app, category, projects, labels_path): if projects: # Gallery Cards - rst, toctree_entries = generate_card_grid(app, rst, projects, labels_path) + rst, toctree_entries = generate_card_grid(app, rst, projects) rst += generate_toctree(toctree_entries) with open(os.path.join(app.builder.srcdir, @@ -229,22 +209,24 @@ def generate_label_buttons(labels): buttons_html += ' \n\n' return buttons_html -def generate_gallery_index(app, category_projects, labels_path): +def generate_gallery_index(app, category_projects): # Main Header gallery_conf = app.config.gallery_conf title = gallery_conf['title'] rst = title + '\n' + '_'*len(title)*3 + '\n' # Overview - INTRO = os.path.join(app.builder.srcdir, f'intro.rst') + INTRO = os.path.join(app.builder.srcdir, 'intro.rst') with open(INTRO, 'r') as file: rst += '\n' + file.read() + '\n\n' # Label Filter Buttons - all_labels = set() + all_labels = [] for sections in category_projects.values(): for section in sections: - all_labels.update(section['labels']) + all_labels.extend(section['labels']) + all_labels = Counter(all_labels) + all_labels = [label for label, _ in all_labels.most_common()] label_buttons_html = generate_label_buttons(all_labels) # Insert the label buttons using raw:: html @@ -263,7 +245,7 @@ def generate_gallery_index(app, category_projects, labels_path): if not projects: continue - rst, _ = generate_card_grid(app, rst, projects, labels_path) + rst, _ = generate_card_grid(app, rst, projects) rst += '\n\n' toctree_entries.append(f'{category} <{category_link}>') diff --git a/_extensions/nbheader.py b/_extensions/nbheader.py index 89d8fd635..5a99280a1 100644 --- a/_extensions/nbheader.py +++ b/_extensions/nbheader.py @@ -28,6 +28,7 @@ def load_authors_mapping(srcdir): def create_header_html( + labels: list[str], authors: list[dict[str, str]], actions: list[dict[str, str]], created_date, @@ -40,6 +41,12 @@ def create_header_html( updated_date = transform_date(updated_date) if updated_date == created_date: updated_date = None + + labels = ''.join([ + f'{label}' + for label in labels + ]) + authors_html = ''.join([ f'''
@@ -62,6 +69,9 @@ def create_header_html( return f'''
+
+ {labels} +
{authors_html}
@@ -175,6 +185,7 @@ def add_nbheader(app): actions.append(download) html_header = create_header_html( + section['labels'], authors_full, actions, header_data['created'], diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css index fa4b938e9..0d8de2a7c 100644 --- a/doc/_static/css/custom.css +++ b/doc/_static/css/custom.css @@ -120,3 +120,8 @@ code.download.literal.xref.docutils { color: #459eb9b3; margin-top: 5px; } + +.hv-gallery-badges .sd-badge { + padding-left: 0.2em; + padding-right: 0.2em; +} diff --git a/doc/_static/js/filter.js b/doc/_static/js/filter.js index fd5d7079d..cc9ad64a6 100644 --- a/doc/_static/js/filter.js +++ b/doc/_static/js/filter.js @@ -11,11 +11,11 @@ document.addEventListener('DOMContentLoaded', function () { allContainers.forEach(cardsContainer => { const cards = Array.from(cardsContainer.getElementsByClassName('sd-col')); cards.forEach(card => { - const labels = card.querySelectorAll('.sd-card-footer img'); + const labels = card.querySelectorAll('span.sd-badge'); let matchedLabels = []; - labels.forEach(labelImg => { - const src = labelImg.src.split('/').pop().split('.')[0]; // Get the filename without extension + labels.forEach(labelBdg => { + const src = labelBdg.textContent if (activeLabels.includes(src)) { matchedLabels.push(src); } diff --git a/doc/conf.py b/doc/conf.py index 040de973c..7a3930bfe 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -153,6 +153,8 @@ def gallery_spec(name): categories = examples_config.get('categories', []) # TODO: isn't optional labels = examples_config.get('labels', []) + # labels always lower cased on the website + labels = list(map(str.lower, labels)) created = examples_config.get('created', 'NA') authors = examples_config['maintainers'] # Optional, computed if not provided. diff --git a/dodo.py b/dodo.py index e3a4b1e24..64f08370c 100644 --- a/dodo.py +++ b/dodo.py @@ -1285,13 +1285,7 @@ def validate_project_file(name): complain(f'{entry!r} must be a list') if not all(isinstance(item, str) for item in value): complain(f'all values of {value!r} must be a string') - if entry == 'labels': - labels_path = pathlib.Path('doc') / '_static' / 'labels' - labels = list(labels_path.glob('*.svg')) - for label in value: - if not any(label_file.stem == label for label_file in labels): - complain(f'missing {label}.svg file in doc/_static/labels') - elif entry == 'categories': + if entry == 'categories': for cat in value: if cat.lower() not in map(str.lower, CAT_TO_CATNAME_MAP): complain( diff --git a/template/anaconda-project.yml b/template/anaconda-project.yml index 259efaa8e..8ca6854e4 100644 --- a/template/anaconda-project.yml +++ b/template/anaconda-project.yml @@ -21,9 +21,6 @@ examples_config: categories: - "Other Sciences" # List of labels displayed in the project card - # Each label must be a name (e.g. panel) that - # refers to a SVG badge located in doc/_static/labels - # (e.g. doc/_static/labels/panel.svg) labels: - "hvplot" - "panel"