Elements and Assets#
Element Catalog#
coco_pipe.report.elements provides 19 reusable HTML primitives.
Every section adder in the report module — and any custom adder you
write — ultimately renders one or more of these. The primitives are
data-first: they accept Python objects (DataFrames, Plotly figures,
strings, byte buffers) and emit standalone HTML.
All elements share a common contract:
Subclass of
Element.render() -> strreturns an HTML fragment.collect_payload(registry)(optional) pushes heavy data into the global registry under a UUID; the rendered fragment references that UUID viadata-id.
—
1. Catalog at a Glance#
Element |
Use |
|---|---|
Raw HTML string passthrough. |
|
Base64-encoded image from bytes, file, |
|
Plotly figure with lazy hydration. |
|
Static HTML table from a DataFrame / dict / list. |
|
Searchable, sortable, paged table with CSV export. |
|
Table with per-column “best” highlighting. |
|
Big-number stat with delta and unit. |
|
Boxed info/warning/error/success message. |
|
Syntax-tagged |
|
Markdown → HTML (graceful fallback). |
|
Pill label with color. |
|
Inline progress bar. |
|
Ordered list of timestamped events. |
|
Tabbed container holding other elements. |
|
|
|
CSS-grid row of side-by-side elements. |
|
Base class for any container; itself adds |
|
|
|
Abstract base; subclass for custom widgets. |
—
2. Data Elements#
2.1 HtmlElement#
Bare HTML wrapper — bypass the typed elements when you need a one-off fragment.
from coco_pipe.report.elements import HtmlElement
sec.add_element(HtmlElement("<div class='text-xs'>raw HTML</div>"))
2.2 ImageElement#
Embeds any image as a base64 data URI. Accepts:
bytes— raw image bytes (any format the browser supports).strorPath— read from disk.Figure— saved at 150 DPI to PNG.
from coco_pipe.report.elements import ImageElement
ImageElement(matplotlib_fig, caption="ROC curve", width="600px")
ImageElement(Path("logo.png"))
ImageElement(raw_bytes) # accepts any browser-supported image format
Every image also emits an inline “Download PNG” link so the reader can extract the asset.
2.3 PlotlyElement#
Plotly figure with lazy hydration: the figure JSON is collected into
the global payload registry; the rendered HTML emits a
<div class="lazy-plot" data-id="…"> placeholder. The browser
hydrates each placeholder when it scrolls into view.
import plotly.graph_objects as go
from coco_pipe.report.elements import PlotlyElement
fig = go.Figure(data=[go.Scatter(x=[1, 2], y=[3, 4])])
sec.add_element(PlotlyElement(fig, height="400px"))
PlotlyElement understands Plotly’s optimization that base64-encodes
large numeric arrays ({"dtype": "f8", "bdata": "…"}) and decodes
them so the rendered JSON is roundtrip-stable.
2.4 TableElement#
Static HTML table. Accepts a DataFrame, a dict (rendered as one-row key/value), a list of records, or a dict of lists.
from coco_pipe.report.elements import TableElement
TableElement(scores_df, title="Per-fold scores")
TableElement({"Accuracy": 0.84, "ROC AUC": 0.91})
TableElement([{"x": 1}, {"x": 2}])
Tables include an inline “Download CSV” button.
2.5 InteractiveTableElement#
Same input as TableElement but client-side searchable,
sortable, paged, with per-column “selector” dropdowns. Useful for
exploratory tables with hundreds of rows.
from coco_pipe.report.elements import InteractiveTableElement
InteractiveTableElement(
big_df,
selector_columns=["dataset", "model"],
default_sort={"column": "score", "direction": "desc"},
page_size=25,
)
2.6 MetricsTableElement#
A TableElement that highlights the “best” value in each
metric column. Pass higher_is_better= to control direction per
column (True / False / per-column list).
from coco_pipe.report.elements import MetricsTableElement
MetricsTableElement(
scores_df,
highlight_cols=["accuracy", "log_loss"],
higher_is_better=["accuracy"], # lower is better for log_loss
)
—
3. UI Primitives#
3.1 StatCardElement#
Large-number card with unit, delta, and a color tag. Use
Report.add_summary_card() to render a row of these at the top of
a report.
from coco_pipe.report.elements import StatCardElement
StatCardElement("Accuracy", 0.95, unit="%", delta="+2.1%", color="green")
3.2 CalloutElement#
Boxed message with an icon. kind is one of "info",
"warning", "error", "success".
from coco_pipe.report.elements import CalloutElement
CalloutElement("Result includes 3 outliers.", kind="warning",
title="Outliers detected")
3.3 CodeBlockElement#
Syntax-tagged <pre><code> block. Adds a “Copy” button when
copyable=True.
from coco_pipe.report.elements import CodeBlockElement
CodeBlockElement(
'reducer.fit(X, y=labels)',
language="python",
title="Reproducer",
copyable=True,
)
3.4 MarkdownElement#
Markdown → HTML using the markdown package when available; falls
back to <pre class="whitespace-pre-wrap"> rendering when not.
from coco_pipe.report.elements import MarkdownElement
MarkdownElement("# Notes\n* Item 1\n* Item 2")
3.5 BadgeElement / ProgressBarElement / TimelineElement#
from coco_pipe.report.elements import (
BadgeElement, ProgressBarElement, TimelineElement,
)
BadgeElement("Experimental", color="red")
ProgressBarElement(value=75, max_value=100, label="Accuracy", color="green")
TimelineElement([
{"title": "Start", "time": "10:00", "description": "Fit reducer"},
{"title": "End", "time": "10:42", "description": "Score complete",
"status": "green"},
])
—
4. Layout Primitives#
4.1 TabsElement#
Tabbed container; keys are tab labels, values are any element.
from coco_pipe.report.elements import TabsElement, PlotlyElement
TabsElement({
"ROC": PlotlyElement(roc_fig),
"PR": PlotlyElement(pr_fig),
"Calibr": PlotlyElement(cal_fig),
})
4.2 AccordionElement#
Collapsible section backed by <details>.
from coco_pipe.report.elements import AccordionElement, CodeBlockElement
acc = AccordionElement("Show training script", open=False)
acc.add_element(CodeBlockElement(open("train.py").read(), language="python"))
sec.add_element(acc)
4.3 ColumnsElement#
CSS-grid row. Sub-elements are placed side by side.
from coco_pipe.report.elements import ColumnsElement, PlotlyElement
ColumnsElement(
[PlotlyElement(roc_fig), PlotlyElement(pr_fig)],
cols=2, gap="gap-4",
)
Section.add_columns() is a shortcut for the same pattern.
4.4 ContainerElement#
Base class. Section and Report inherit from it.
Useful when subclassing for custom containers.
—
5. Download Helpers#
DownloadAssetElement embeds binary or text payloads as base64 data
and produces a <a download> link. Useful for shipping the source
data alongside the chart.
from coco_pipe.report.elements import DownloadAssetElement
DownloadAssetElement(
data=csv_bytes,
filename="scores.csv",
mime_type="text/csv",
label="Download per-fold scores",
style="gray",
)
The payload is captured into the global registry and decoded on-demand when the user clicks; the HTML stays small.
—
6. Custom Elements#
To add a new element type, subclass
Element:
from coco_pipe.report.elements import Element
class KPIElement(Element):
def __init__(self, label, value):
self.label, self.value = label, value
def render(self) -> str:
return (
f"<div class='kpi'>"
f"<span class='label'>{self.label}</span>"
f"<span class='value'>{self.value}</span>"
f"</div>"
)
If your element holds heavy data, override collect_payload and use
data-id="…" in the rendered fragment. See
PlotlyElement and DownloadAssetElement for canonical
implementations.
See Custom Elements and Adders for binding custom adders to Report.
JavaScript Asset Modes#
Rendered reports rely on three JavaScript bundles:
Plotly — interactive plot rendering.
Tailwind Play CDN — runtime CSS compiler driven by the markup.
pako — gzip decompression of the embedded data payload.
How those bundles are served is controlled by the asset_urls
constructor argument on Report and
every factory in coco_pipe.report.api.
—
1. Three Modes#
|
Behavior |
|---|---|
|
|
|
CDN defaults merged with the user’s overrides. |
|
The bundles are downloaded once (with caching) and embedded directly in |
The current mode is exposed on the report as Report.asset_mode
("cdn" / "custom" / "inline") and surfaced in the Run
Info drawer.
—
2. CDN Mode (Default)#
from coco_pipe.report import Report
report = Report(title="Online")
report.save("online.html")
The rendered HTML loads:
https://cdn.plot.ly/plotly-2.27.0.min.jshttps://cdn.tailwindcss.comhttps://cdnjs.cloudflare.com/ajax/libs/pako/2.1.0/pako.min.js
Pros: nothing to host, small HTML. Cons: requires network when the report is opened.
—
3. Self-Hosted URLs#
Pass a dict to point at your own bundle URLs:
report = Report(
title="Self-hosted",
asset_urls={
"plotly": "/static/plotly-2.27.0.min.js",
"tailwind": "/static/tailwind.min.js",
"pako": "/static/pako-2.1.0.min.js",
},
)
Only override the URLs you want to change; unspecified slots fall back
to the CDN defaults. The mode is set to "custom".
Useful when your team intranet hosts vendored JS, or when you need a specific Plotly version different from the default.
—
4. Inline Mode (Fully Offline)#
report = Report(title="Air-gapped", asset_urls="inline")
report.save("standalone.html")
On the first run, get_vendored_contents()
downloads the three bundles into
~/.cache/coco-pipe/report-assets/ and reads them as strings. The
template then inlines them in <script>...</script> tags instead of
<script src=...>. Subsequent runs skip the download and just read
from cache.
The resulting HTML is fully self-contained — opens identically on a laptop with no internet, an air-gapped review machine, or a printed- PDF reference copy.
4.1 Cache location#
The cache directory defaults to
~/.cache/coco-pipe/report-assets/. Override it via environment
variable when generating reports on a shared cluster:
export COCO_PIPE_REPORT_ASSET_CACHE=/scratch/coco-pipe-assets
4.2 Pre-warming the cache#
For automated pipelines running on a machine without internet, run the download step once on a machine that does:
from coco_pipe.report._assets import vendor_assets
cache_dir = vendor_assets() # downloads if missing
cache_dir = vendor_assets(force=True) # re-download
Then ship the cache directory alongside the code base, set the env
var to its path on the target machine, and asset_urls="inline"
will use the local copies without ever touching the network.
4.3 Cost#
Inline mode adds roughly:
Bundle |
Approximate size |
|---|---|
Plotly |
~3 MB minified |
Tailwind Play CDN |
~70 KB |
pako |
~50 KB |
So a typical inline report grows by ~3-4 MB compared to a CDN report. Worth it when offline access matters; skip when it doesn’t.
—
5. Failure Modes#
Scenario |
Behavior |
|---|---|
Network unavailable in CDN mode |
Report renders fine; opens blank or broken in the browser. Switch to |
Network unavailable in inline mode |
First call raises |
Unknown |
|
Cache directory not writable |
First call raises |
—
6. Asset Mode at a Glance — When to Use Which#
Use case |
Recommended mode |
|---|---|
Interactive notebook exploration |
|
Web-served reports |
|
Email / Slack-shared HTML |
|
Long-term archive / publication |
|
Air-gapped / cluster runs |
|