.. _report-advanced: =============== Advanced Topics =============== .. _report-templates: Templates and Layout Customization ================================== The report HTML is produced by Jinja templates living under ``coco_pipe/report/templates/``. The default layout is opinionated but intentionally easy to extend or replace — you can override individual partials, swap the entire base template, or build a fresh layout from the published Element catalog. --- 1. Template Layout ------------------ After the file-split refactor, the templates tree is:: coco_pipe/report/templates/ ├── base.html thin orchestrator (~70 lines) ├── section.html renders one Section ├── partials/ │ ├── header.html top nav bar │ ├── sidebar.html TOC, search, filter, findings summary │ ├── report_summary.html counts strip at top of main │ └── info_drawer.html slide-out Run Info panel └── static/ ├── tailwind_config.html inline Tailwind config `` content. 2. Apply the loader override from §3. Common tweaks: - Override the dark-mode default (search for ``color-theme``). - Change the Plotly modeBar buttons (search for ``modeBarButtonsToAdd``). - Adjust the lazy-plot intersection rootMargin (``200px`` default). --- 6. Custom Section Templates --------------------------- The default ``section.html`` works for every Element type because all content goes through ``{{ content | safe }}``. If you need section-specific markup (different status pill colors, an extra metadata strip), override ``section.html`` using the same loader trick. Alternative: subclass :class:`~coco_pipe.report.core.Section` and override ``render()`` to point at your own template: .. code-block:: python from coco_pipe.report.core import Section from coco_pipe.report._engine import render_template class FlaggedSection(Section): """A Section that renders with our 'flagged' template.""" def render(self) -> str: return render_template( "partials/flagged_section.html", title=self.title, id=self.id, status=self.status, flag_color=getattr(self, "flag_color", "amber"), content=self.render_children(), ) The new ``partials/flagged_section.html`` lives in your overrides directory (or in the package once merged); Jinja resolves it through the loader chain. --- 7. Theming and Tailwind ----------------------- The template uses Tailwind's Play CDN, which JITs CSS from the markup at load time. To customize colors, edit ``static/tailwind_config.html`` — specifically the ``brand`` and ``coco`` palettes in ``tailwind.config.theme.extend.colors``. For production deployments where the runtime CSS compiler isn't an option: 1. Pre-build the CSS bundle using the Tailwind CLI on the rendered HTML. 2. Replace the ``tailwindcss`` script with the pre-built CSS via the ``asset_urls`` override. 3. Ship the resulting standalone HTML. This is the most reliable path for embedding reports in environments that disallow eval-style runtime CSS (some strict CSP setups). --- 8. Print Stylesheet ------------------- ``static/print_styles.html`` contains ``@media print`` rules that: - Hide the sidebar, info drawer, Back-to-top button, and other controls. - Reset main margins for full-width page output. - Force light backgrounds (cheaper toner and better OCR). - Keep sections from breaking mid-figure. To export a printable PDF, open the report in Chromium-family browsers and use "Save as PDF" from the print dialog — the print styles take over automatically. .. _report-extensions: Custom Elements and Adders ========================== The report module is built so that custom widgets and custom section adders compose with the shipped ones using the same patterns. This page walks through the three common extension points. --- 1. Custom :class:`Element` Subclasses ------------------------------------- Every primitive in :mod:`~coco_pipe.report.elements` is a subclass of the abstract :class:`~coco_pipe.report.elements.Element`. To add your own, override two methods at most. 1.1 Stateless element ~~~~~~~~~~~~~~~~~~~~~ If your element doesn't hold heavy data, ``render()`` is all you need: .. code-block:: python from coco_pipe.report.elements import Element class KPICard(Element): """Big stat with a sub-label, no payload registration.""" def __init__(self, label: str, value: str, sub: str = ""): self.label, self.value, self.sub = label, value, sub def render(self) -> str: return ( "