Skip to content

Commit

Permalink
doc: Introduce boards catalog
Browse files Browse the repository at this point in the history
This commit adds support for generating an interactive catalog of all
the supported boards that can be included in the documentation using
the `.. zephyr:board-catalog::` directive.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
  • Loading branch information
kartben authored and nashif committed Oct 10, 2024
1 parent 4eb415e commit f2f1496
Show file tree
Hide file tree
Showing 8 changed files with 599 additions and 7 deletions.
18 changes: 12 additions & 6 deletions boards/index.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
.. _boards:

Supported Boards
################

Zephyr project developers are continually adding board-specific support as
documented below.
Supported Boards and Shields
############################

If you are looking to add Zephyr support for a new board, please start with the
:ref:`board_porting_guide`.

When adding support documentation for each board, remember to use the template
When adding support documentation for a board, remember to use the template
available under :zephyr_file:`doc/templates/board.tmpl`.

Shields are hardware add-ons that can be stacked on top of a board to add extra
functionality. They are listed separately from boards, towards :ref:`the end of
this page <boards-shields>`.

Use the interactive search form below to quickly navigate through the list of
supported boards.

.. toctree::
:maxdepth: 2
:glob:
:hidden:

*/index

.. zephyr:board-catalog::
.. _boards-shields:

Shields
Expand Down
42 changes: 41 additions & 1 deletion doc/_extensions/zephyr/domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- ``zephyr:code-sample::`` - Defines a code sample.
- ``zephyr:code-sample-category::`` - Defines a category for grouping code samples.
- ``zephyr:code-sample-listing::`` - Shows a listing of code samples found in a given category.
- ``zephyr:board-catalog::`` - Show a listing of boards supported by Zephyr.
Roles
-----
Expand All @@ -23,9 +24,10 @@
"""

import sys
from os import path
from pathlib import Path
from typing import Any, Dict, Iterator, List, Tuple
from typing import Any, Dict, Iterator, List, Tuple, Final

from docutils import nodes
from docutils.parsers.rst import Directive, directives
Expand All @@ -42,6 +44,7 @@
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.nodes import NodeMatcher, make_refnode
from sphinx.util.parsing import nested_parse_to_nodes
from sphinx.util.template import SphinxRenderer

from zephyr.doxybridge import DoxygenGroupDirective
from zephyr.gh_utils import gh_link_get_url
Expand All @@ -53,6 +56,14 @@

__version__ = "0.2.0"

ZEPHYR_BASE = Path(__file__).parents[4]

sys.path.insert(0, str(ZEPHYR_BASE / "scripts/dts/python-devicetree/src"))
sys.path.insert(0, str(Path(__file__).parents[3] / "_scripts"))

from gen_boards_catalog import get_catalog

TEMPLATES_DIR = Path(__file__).parent / "templates"
RESOURCES_DIR = Path(__file__).parent / "static"

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -558,6 +569,25 @@ def run(self):
return [code_sample_listing_node]


class BoardCatalogDirective(SphinxDirective):
has_content = False
required_arguments = 0
optional_arguments = 0

def run(self):
if self.env.app.builder.format == "html":
self.env.domaindata["zephyr"]["has_board_catalog"][self.env.docname] = True

# As it is not expected that more than one board-catalog directive is used across
# the documentation, and since the generation is only taking a few seconds, we don't
# store the catalog in the domain data. It might change in the future if the generation
# becomes more expensive.
board_catalog = get_catalog()
renderer = SphinxRenderer([TEMPLATES_DIR])
rendered = renderer.render("board-catalog.html", {"catalog": board_catalog})
return [nodes.raw("", rendered, format="html")]


class ZephyrDomain(Domain):
"""Zephyr domain"""

Expand All @@ -573,6 +603,7 @@ class ZephyrDomain(Domain):
"code-sample": CodeSampleDirective,
"code-sample-listing": CodeSampleListingDirective,
"code-sample-category": CodeSampleCategoryDirective,
"board-catalog": BoardCatalogDirective,
}

object_types: Dict[str, ObjType] = {
Expand All @@ -586,6 +617,7 @@ class ZephyrDomain(Domain):
"code-samples-categories-tree": Node("samples"),
# keep track of documents containing special directives
"has_code_sample_listing": {}, # docname -> bool
"has_board_catalog": {}, # docname -> bool
}

def clear_doc(self, docname: str) -> None:
Expand All @@ -604,6 +636,7 @@ def clear_doc(self, docname: str) -> None:
# TODO clean up the anytree as well

self.data["has_code_sample_listing"].pop(docname, None)
self.data["has_board_catalog"].pop(docname, None)

def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
self.data["code-samples"].update(otherdata["code-samples"])
Expand All @@ -626,6 +659,10 @@ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
self.data["has_code_sample_listing"][docname] = otherdata[
"has_code_sample_listing"
].get(docname, False)
self.data["has_board_catalog"][docname] = otherdata["has_board_catalog"].get(
docname, False
)

def get_objects(self):
for _, code_sample in self.data["code-samples"].items():
yield (
Expand Down Expand Up @@ -761,6 +798,9 @@ def install_static_assets_as_needed(
app.add_js_file("js/codesample-livesearch.js")

if app.env.domaindata["zephyr"]["has_board_catalog"].get(pagename, False):
app.add_css_file("css/board-catalog.css")
app.add_js_file("js/board-catalog.js")


def setup(app):
app.add_config_value("zephyr_breathe_insert_related_samples", False, "env")
Expand Down
214 changes: 214 additions & 0 deletions doc/_extensions/zephyr/domain/static/css/board-catalog.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/**
* Copyright (c) 2024, The Linux Foundation.
* SPDX-License-Identifier: Apache-2.0
*/

.hidden {
display: none !important;
}

.filter-form {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}

.filter-form input,
.filter-form select {
appearance: none;
font-family: var(--system-font-family);
font-size: 14px;
border-radius: 50px;
padding: 10px 18px;
flex: 1 1 200px;
background-color: var(--input-background-color);
color: var(--body-color);
transition: all 0.3s ease;
box-shadow: none;
}

.filter-form input:focus .filter-form select:focus {
border-color: var(--input-focus-border-color);
}
.select-container {
flex: 1 1 200px;
position: relative;
}

.select-container::after {
content: "\25BC";
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
font-size: 14px;
color: var(--body-color);
}

.filter-form select {
padding-right: 40px;
width: 100%;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}

#catalog {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
margin-top: 20px;
margin-bottom: 40px;
}

.board-card {
flex: 1 1 calc(33.3% - 20px);
/* Three cards per row */
max-width: calc(33.3% - 20px);
border-radius: 8px;
padding: 15px 20px;
background-color: var(--admonition-note-background-color);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
align-items: center;
transition: transform 0.3s ease;
}

.board-card:hover,
.board-card:focus {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-5px);
text-decoration: none;
}

.board-card .picture {
width: auto;
height: auto;
min-height: 100px;
max-height: 180px;
border-radius: 4px;
margin: 0 auto;
display: flex;
align-items: center;
flex-grow: 1;
padding: 10px 0px;
}

.board-card .no-picture {
font-size: 5em;
color: var(--admonition-note-title-background-color);
justify-content: center;
}

.board-card .vendor {
font-size: 12px;
color: var(--admonition-note-color);
font-weight: 900;
margin-bottom: 18px;
opacity: 0.5;
}

.board-card img {
max-height: 100%;
max-width: 100%;
object-fit: contain;
}

.board-card .board-name {
font-family: var(--header-font-family);
margin: auto 0 5px;
text-align: center;
font-size: 18px;
font-weight: 500;
color: var(--body-color);
padding-top: 10px;
}

.board-card .arch {
margin: 5px 0;
text-align: center;
font-size: 12px;
font-weight: 100;
color: var(--body-color);
}

@media (max-width: 1024px) {
.board-card {
flex: 1 1 calc(50% - 20px);
max-width: calc(50% - 20px);
}
}

@media (max-width: 768px) {
.board-card {
flex: 1 1 calc(100% - 20px);
max-width: calc(100% - 20px);
}

.board-card .picture {
min-height: 60px;
max-height: 120px;
}
}

#form-options .btn {
font-size: 14px;
}

#form-options .btn:focus {
outline-color: var(--body-color) !important;
}

#catalog.compact {
display: block;
list-style-type: disc;
margin: 20px;
}

#catalog.compact .board-card {
display: list-item;
padding: 4px 0;
border: none;
background-color: transparent;
box-shadow: none;
list-style-position: outside;
max-width: none;
}

#catalog.compact .board-card .vendor,
#catalog.compact .board-card .picture {
display: none;
}

#catalog.compact .board-card .board-name {
display: inline;
font-family: var(--system-font-family);
text-align: left;
font-size: 16px;
font-weight: normal;
color: var(--body-color);
margin: 0;
padding: 0;
}

#catalog.compact .board-card .arch {
display: inline;
}

#catalog.compact .board-card .arch::before {
content: " (";
}

#catalog.compact .board-card .arch::after {
content: ")";
}

#catalog.compact .board-card:hover {
box-shadow: none;
transform: none;
text-decoration: underline;
}
Loading

0 comments on commit f2f1496

Please sign in to comment.