{% extends "layout.html" %} {% from "_partials/table_helpers.html" import search_input, sort_header, pagination_inline %} {% from "_partials/recent_events.html" import recent_events_card %} {% from "_partials/page_description.html" import page_description %} {% block title %}Catalog - pixie{% endblock %} {% block subnav %} {% endblock %} {% block content %} {{ page_description('journal-code', 'Images pixie can flash or netboot. Add an entry with a source URL, then fetch it to pull the bytes onto pixie. An entry is bindable once fetched; deletion is safe only after every machine pointing at it is rebound.') }}
{# Card header: title/label on the left, filter form inline in the middle, pagination + per-page on the right -- same toolbar shape bty uses on /ui/events. #}
Catalog {{ search_input('/ui/catalog', q, preserved, 'name / src / arch / format / description') }} {{ pagination_inline(page_state, preserved, label='entries') }}
{% if entries %}
{{ sort_header('Entry', 'name', sort, preserved) }} {{ sort_header('Type', 'format', sort, preserved) }} {% for e in entries %} {% set st = fetch_states.get(e.name) or {} %} {% set exp = exports_by_sha.get(e.content_sha256) if e.content_sha256 else none %} {% endfor %}
Fetch state NBD Actions
{% if e.description %}
{{ e.description }}
{% endif %}
{{ e.src }}
{% if e.netboot_src %}
netboot bundle: {{ e.netboot_src }}
{% endif %}
{% if e.bindable %} Disk image {% else %} Netboot bundle {% endif %}
{{ e.format }} {% if e.arch %}{{ e.arch }}{% endif %}
{% if st.state == "fetching" %} {# Live phase pill: server-rendered starting state; the poll script below rewrites this cell's inner HTML every 2s when there's an in-flight fetch. Phases in the sub-state dict: ``downloading`` (+bytes_downloaded / total_bytes), ``decompressing`` (+format), ``unpacking`` (netboot bundles). #} {% if st.phase == "downloading" %} downloading {% if st.total_bytes %}
{{ st.bytes_downloaded or 0 }} / {{ st.total_bytes }} bytes
{% elif st.bytes_downloaded %}
{{ st.bytes_downloaded }} bytes so far
{% endif %} {% elif st.phase == "decompressing" %} decompressing {% if st.format %}
{{ st.format }}
{% endif %} {% elif st.phase == "unpacking" %} unpacking {% else %} fetching… {% endif %} {% if st.started_at %}
since {{ st.started_at | fmt_ts }}
{% endif %} {% elif st.state == "error" %} error {% if st.error %}
{{ st.error }}
{% endif %} {% elif st.state == "unchanged" %} {# The operator clicked Update but the entry was already at the current sha and the on-disk bytes were intact. The pill lingers ~8 s (the handler auto-clears it) so the click has a visible acknowledgement, then the row falls back to the plain "fetched" badge. #} already at latest {% if e.content_sha256 %}
{{ e.content_sha256[:12] }}
{% endif %} {% elif e.content_sha256 %} fetched
{{ e.content_sha256[:12] }}
{% if e.size_bytes %}
{{ e.size_bytes }} bytes
{% endif %} {% else %} not fetched {% endif %}
{% if not e.bindable %} (HTTP artifact) {% elif exp is none %} not serving {% elif exp.status == 'running' %} running
port {{ exp.nbd_port }}
{% elif exp.status == 'error' %} error {% if exp.error %}
{{ exp.error }}
{% endif %} {% else %} {{ exp.status }} {% endif %}
{# All three action buttons are ALWAYS rendered so an operator's eye finds them at the same X position in every row. Verb + enabled state track the row's current state: no bytes on disk -> Fetch; a prior fetch failed -> Retry; already have bytes -> Update (re-runs the fetch pipeline; picks up a moved oras:// tag's new manifest); in-flight fetch -> Fetching (disabled). Update is enabled because labelling a fetched entry as "Fetch" makes an operator instinctively hit it thinking bytes are missing. Stop is disabled when there is no NBD export to stop; Delete is always enabled. #} {% set fetching = st.state == 'fetching' %} {% set fetch_errored = st.state == 'error' %} {% set already_fetched = e.content_sha256 and not fetch_errored %}
{% else %}

{% if q %} No entries match {{ q }}. {% else %} No entries yet. Paste a catalog URL in the bar above and hit Import; entries land here staged (not fetched) so you can pick the ones to pull with Fetch. {% endif %}

{% endif %}
{{ recent_events_card(catalog_events, "catalog") }} {% endblock %}