{# Shared chart controls for the trial and task-comparison pages. Each macro takes `url`, a macro of `(trace filter, chart filter) -> href`, so each page owns its query keys while sharing the chart markup. The numbers arrive already placed in SVG user units from `app/nullius/traces/charts.py`, which computes them in Python because the arithmetic runs on nanosecond magnitudes a JS double cannot hold. #} {# The label filter's chips. Built from the *unfiltered* tree, so a hidden label keeps the chip that brings it back. #} {% macro facet_chips(facets, filt, chart_filter, url) %}
{% for f in facets %} {{ f.name }} {{ f.count }} {% endfor %}
{% endmacro %} {# The four charts, their legends, and the paragraph that stops them being misread as an accounting of the run. `empty_note` explains why the current page has nothing to plot. `tail` closes the paragraph that says summed span time is not wall clock. The trial page points from there at the Time breakdown strip below it; a page without that strip must not claim one. #} {% macro charts_panel(charts, chart_filter, filt, url, empty_note="", tail="") %} {% set box = charts.box %}

Distributions · the shape of the calls, not the timeline

{% if charts.has_spans %}
{% for c in charts.items %}
{{ c.title }} {{ c.subtitle }} {% if c.head %} {{ c.head.ns|nsround if c.head.ns is not none else c.head.text }} {{ c.head.unit }}{% if c.head.lo_ns is not none %} {{ c.head.lo_ns|nsround }}–{{ c.head.hi_ns|nsround }}{% endif %} {% endif %}
{# Stretched to the column (`none`): `non-scaling-stroke` keeps every line one weight however wide that is, and the bars and labels are page elements, so nothing distorts. #} {% for t in c.yticks %}{{ t.text }}{% endfor %} {% if c.marker %} {% set m_at = box.left(c.marker.at) %}
{{ c.marker.word }} {{ c.marker.ns|nsround }}
{% endif %} {% if c.form == 'histogram' %}
{% for col in c.columns %} {% set at = box.left(col.x) %} {% endfor %}
{% else %}
{% for r in c.readouts %} {% set at = box.left(r.x) %} {% endfor %}
{% endif %}
{% for t in c.xticks %}{{ t.ns|nsround }}{% endfor %}

How long one call took{% if c.x_step %}, each mark {{ c.x_step }}× the last{% endif %}

{# A control, not a key: each chip drops its kind from *this* chart and the chart rescales to what is left. Its own query key, because it changes one chart and not the trace — the waterfall, the breakdown and save-trace are all blind to it. #} {% if c.kinds %}
{% for k in c.kinds %} {{ k.kind }} {{ k.n }} {% endfor %} {% if c.hidden_kinds %} all × {% endif %}
{% endif %} {% if c.key_kinds|length > 1 %}
{% for k in c.key_kinds %}{{ k.kind }}{% endfor %}
{% endif %} {% if c.note %}

{{ c.note }}

{% endif %}
{% endfor %}
{# The one thing these charts could be misread as. #}

These durations are how long each call lasted, not moments in the run. The {{ charts.summed_ns|nsdur }} they add up to is not wall clock: calls overlap and leave gaps. {{ tail }} {% if charts.note %}{{ charts.note }}{% endif %} {% if chart_filter.active %} Draw every kind again × {% endif %}

Values {% for c in charts.items %} {% if c.form == 'histogram' %} {% if c.columns and c.columns[0].share %}{% endif %}{% for col in c.columns %}{% if col.share %}{% endif %}{% endfor %} {% else %} {% for s in c.series %}{% endfor %}{% for r in c.readouts %}{% for row in r.rows %}{% endfor %}{% endfor %} {% endif %}
{{ c.title }}
DurationSpansSpan timeShare
{{ col.lo_ns|nsround }}–{{ col.hi_ns|nsround }}{{ col.count }}{{ col.ns|nsdur }}{{ col.share }}
No longer than{{ s.label }}
{{ r.ns|nsround }}{{ row.share }}
{% endfor %}
{% else %}

{% if empty_note %}{{ empty_note }} {% elif charts.total %}None of the {{ charts.total }} span{{ '' if charts.total == 1 else 's' }} in this view is a call with a known duration — there is nothing to plot. {% else %}Every span is filtered out — nothing left to plot.{% endif %}

{% endif %}
{% endmacro %}