{# Console 2.0 speakers overview (#159) — 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. #} {% set qs = "sort=" ~ sort ~ "&view=" ~ view %} {% if roster_error %}
{{ roster_error }}
{% endif %} {# Action reminders (#159): the review work waiting on this roster. Capped lists with honest "and N more" totals; the duplicates card is deferred to a follow-up issue. #} {% if overview.name_suggestions or overview.unverified_active %}{{ overview.name_suggestion_total }} speaker-name suggestion{{ "s" if overview.name_suggestion_total != 1 }} waiting for review: {% for reminder in overview.name_suggestions %} {% if reminder_runs.get(reminder.run_id) %}{{ run_source_title(reminder_runs[reminder.run_id]) }}{% else %}a recording{% endif %} ({{ reminder.count }}){{ ", " if not loop.last }} {% endfor %} {%- if overview.name_suggestion_total > overview.name_suggestions|sum(attribute="count") %} and more on other recordings {%- endif %}
{% endif %} {% if overview.unverified_active %}{{ overview.unverified_active_total }} frequently heard voice{{ "s" if overview.unverified_active_total != 1 }} you haven't confirmed yet: {% for row in overview.unverified_active %} {{ row.entry.speaker.display_name }} ({{ format_duration(row.aggregate.seconds) }}){{ ", " if not loop.last }} {% endfor %} {%- if overview.unverified_active_total > overview.unverified_active|length %} and {{ overview.unverified_active_total - overview.unverified_active|length }} more {%- endif %}
{% endif %}{{ overview.rows|length }} speaker{{ "s" if overview.rows|length != 1 }} · {{ overview.verified_count }} verified · {{ format_duration(overview.total_seconds) }} attributed across {{ overview.runs_scanned }} processed recording{{ "s" if overview.runs_scanned != 1 }}
{% macro tier_chip(row) %} {%- if row.tier.tier -%} {{ row.tier.tier }} voice match {%- elif row.tier.has_voice_evidence -%} match details unavailable {%- else -%} no voice evidence {%- endif -%} {% endmacro %} {% macro verified_badge(row) %} {%- if row.aggregate.verified -%}Verified{%- endif -%} {% endmacro %} {% macro actions(row) %} {% set targets = overview.rows | rejectattr("entry.speaker.id", "equalto", row.entry.speaker.id) | list %} {% if targets %} {% endif %} {% endmacro %} {% if view == "table" %}| Name | Files | Minutes | Last heard | Voice match | Actions |
|---|---|---|---|---|---|
| {{ row.entry.speaker.display_name }} {{ verified_badge(row) }} | {{ row.aggregate.files }} | {{ format_duration(row.aggregate.seconds) }} | {% if row.aggregate.last_seen %}{{ format_age(row.aggregate.last_seen, now=now) }}{% else %}never{% endif %} | {{ tier_chip(row) }} | {{ actions(row) }} |
{{ 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 in a processed recording{% endif %}
{{ tier_chip(row) }}
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 %}