{% macro run_row(name, status, oob=false, since=none, elapsed=none) %} {{ name }} {%- if status == 'Finished' %}Finished {%- elif status == 'Failed' %}Failed {%- elif status == 'Queued' %}Queued {%- else %}{{ status }}{% endif %} {%- if status in ('Finished', 'Failed') %}{{ (elapsed | duration) if elapsed is not none else '—' }} {%- elif status != 'Queued' and since is not none %}0s {%- else %}{% endif %} {% endmacro %} {# Compose-style row — a faithful reproduction of the CLI's `-x` / --compose-style output (src/rosbackup/output/compose.py): " ", 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) %} {% 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 %}