{# Console 2.0 speakers overview (#159, R4 #213) — full-page include and the htmx post-mutation refresh target (#roster). Numbers come from effective resolution (the canonical-run fold), never raw machine proposals. Every mutating form's action carries the current ?sort/?view so its re-render preserves both. Operator refusals (RosterError) arrive as roster_error. #245: the roster splits into two visual groups — verified ("Known speakers") and unverified ("Unnamed voices", collapsed by default). Heard-name annotation shows the machine-proposed name for unverified speakers. #} {% from "fragments/_chips.html" import chip %} {% set qs = "sort=" ~ sort ~ "&view=" ~ view %} {% if roster_error %}
{{ roster_error }}
{% endif %} {# Mutation CSRF tokens: the overview's action forms live on the detail page, but the route handler re-renders this fragment after mutations, so the tokens must survive here for htmx round-trips. #} {# TO DO strip (#159 R4): action reminders the operator needs to address. #} {% if overview.unverified_active or overview.name_suggestions or overview.possible_duplicates %}{{ row.aggregate.files }} file{{ "s" if row.aggregate.files != 1 }} · {{ format_duration(row.aggregate.seconds) }} · {% if row.aggregate.last_seen %}last heard {{ format_age(row.aggregate.last_seen, now=now) }}{% else %}never heard{% endif %}
{{ match_pct(row) }}
No verified speakers yet. Name a voice during review to add someone here.
{% endif %} {# === Unnamed voices (unverified, collapsed by default) === #} {% if unverified_rows %}No speakers yet. Voices join the roster when you name them while reviewing a recording.
{% endif %} {% if overview.inactive %}{{ entry.speaker.display_name }} — {% if entry.merged_into_name %} merged into {{ entry.merged_into_name }} {% else %} archived
{% endif %} {% endfor %}