", only the symbol is
coloured (✔ green / ✘ red / spinner by phase). Same id as run_row so the SSE feed
OOB-swaps it in place; app.js animates the braille spinner + ticks the elapsed. #}
{% macro compose_row(name, status, oob=false, since=none, elapsed=none, detail='') %}
{%- set term = status in ('Finished', 'Failed') -%}
{%- set state = 'ok' if status == 'Finished' else ('bad' if status == 'Failed' else ('queued' if status in ('Waiting', 'Queued') else 'running')) -%}
{%- set sym = 'cyan' if status in ('Starting', 'Connecting') else ('blue' if status in ('Getting Info', 'Backing Up') else ('magenta' if status == 'Downloading' else ('dim' if status in ('Waiting', 'Queued') else 'yellow'))) -%}
{{ '✔' if status == 'Finished' else ('✘' if status == 'Failed' else '⠋') }}
{{ name }}
{{ status }}
{{ detail or '' }}
{{ ('%.1f'|format(elapsed) ~ 's') if elapsed is not none else ('' if term else '0.0s') }}
{% endmacro %}
{# Compose-style aggregate dashboard for large fleets (mirrors ComposeProgress._render_
aggregate): progress bar + counts + a window of currently-running rows + a failures tail.
The whole block OOB-swaps as #compose-agg on each event. #}
{% macro compose_aggregate(stats, oob=false, verb='Backing up') %}
[+] {{ verb }} {{ stats.total }} devices
{{ '█' * stats.filled }}{{ '░' * stats.empty }} {{ stats.done }}/{{ stats.total }} {{ '%4.0f'|format(stats.pct) }}% ✔ {{ stats.success }} ✘ {{ stats.failed }} ⠋ {{ stats.running }} running {{ stats.elapsed_str }}{% if stats.eta_str %} eta {{ stats.eta_str }}{% endif %}
{%- if stats.active %}
Active:
{% for a in stats.active %}
⠋{{ a.txt }}
{% endfor %}
{%- if stats.hidden > 0 %}
… and {{ stats.hidden }} more running
{% endif %}
{%- endif %}
{%- if stats.failures %}
Recent failures:
{{ stats.failures | join(', ') }}
{%- endif %}
{% endmacro %}
{# Compose-style summary block (mirrors ComposeProgress.finish_summary; counts coloured). #}
{% macro compose_summary(summary, size_str='', oob=false) %}
{%- if summary %}
Summary:
Total time: {{ '%.1f'|format(summary.duration_s or 0) }}s
Total size: {{ size_str }}
Success: {{ summary.success }} | Failed: {{ summary.failed }} | Total: {{ summary.total }}
{%- if summary.failed_targets %}
Failed devices: {{ summary.failed_targets | join(', ') }}
{% endif %}
{%- endif %}
{% endmacro %}
{% macro run_error(detail, oob=false) %}
Run error: {{ detail }}
{% endmacro %}
{% macro run_summary(summary, oob=false) %}
{%- if summary %}
{{ summary.success }}/{{ summary.total }} succeeded
{{ summary.duration_s | duration }}
{%- if summary.failed_targets %}failed: {{ summary.failed_targets | join(', ') }}{% endif %}
{%- endif %}
{% endmacro %}