{# ABOUTME: Plan-01 paginated list partial — chip strip, count label, sortable table, pager. #} {# Rendered standalone for htmx swaps into #book-list. #} {% include "_status_filter.html" %} {% include "_filter_chips.html" %} {% if page.total > 0 %}

Showing {{ page.start }}–{{ page.end }} of {{ page.total }}

{# Sortable header link macro. Clicking the active column flips direction; clicking an inactive column starts at ``asc``. Page resets to 1 on any sort change — the user has new ordering, the old page index is stale. Active filters ride along so the user's filter set survives a re-sort. #} {% macro sort_link(label, key) -%} {%- set is_active = (query.sort == key) -%} {%- set next_dir = 'desc' if (is_active and query.dir == 'asc') else 'asc' -%} {{ label }}{% if is_active %} {% endif %} {%- endmacro %} {# P3 (#183) — bulk-mark form lives outside the table so the checkbox inputs inside each row can target it via the ``form="…"`` attribute, keeping the table markup flat and the action bar reusable across the desktop table and the mobile card list. #}
{% if 'series' in visible_columns %}{% endif %} {% if 'isbn' in visible_columns %}{% endif %} {% if 'language' in visible_columns %}{% endif %} {% if 'publisher' in visible_columns %}{% endif %} {% if 'added' in visible_columns %}{% endif %} {% if 'enriched' in visible_columns %}{% endif %} {% for book in page.books %} {% if 'series' in visible_columns %}{% endif %} {% if 'isbn' in visible_columns %}{% endif %} {% if 'language' in visible_columns %}{% endif %} {% if 'publisher' in visible_columns %}{% endif %} {% if 'added' in visible_columns %}{% endif %} {% if 'enriched' in visible_columns %}{% endif %} {% endfor %}
{{ sort_link('Title', 'title') }} {{ sort_link('Author', 'author') }}{{ sort_link('Series', 'series') }}ISBNLanguagePublisher{{ sort_link('Added', 'added') }}Enriched
{%- set status = book_statuses.get(book.id) -%} {%- if status and status.status == 1 -%} 📖 {%- elif status and status.status == 2 -%} ✓ {%- endif %} {{ book.metadata.title }} {{ book.metadata.author }}{% if book.metadata.series %}{{ book.metadata.series }}{% if book.metadata.series_index is not none %} #{{ '%g'|format(book.metadata.series_index) }}{% endif %}{% endif %}{{ book.metadata.isbn or '' }}{{ book.metadata.language or '' }}{{ book.metadata.publisher or '' }}{{ book.date_added or '' }}{% if book.metadata_matched_at %}✓{% endif %} {# Native
menu, styled locally (not Pico's .dropdown, which is select-shaped). Edit and Enrich are plain navigations to their full pages; Delete loads the shared confirm partial into the page-level #row-dialog. #}
⋮
{# Mobile-only stacked card layout. Same data, different shell — CSS at 768px hides the table and shows this block. Whole card is one link so the entire row is tappable on touch devices. #} {% if page.total_pages > 1 %} {% endif %} {% elif query.filters %} {# Filtered-to-zero state. Preserve the search query in the clear-filters link so a user who typed a query and then filtered into empty results can drop only the filters, not their search. #}

No books match these filters.

Clear filters

{% elif query.q %}

No books matching '{{ query.q }}'

{% else %}

Your library is empty. Run bookery import to add books.

{% endif %}