umbral-admin 0.0.12

Auto-generated CRUD admin UI for umbral models.
Documentation
{#
  feed.html
  =========
  Activity feed widget renderer.
  Expects payload: { items: [{actor, verb, object, object_link?, at}] }
#}

{% macro feed_widget(title, payload) %}
<div class="feed-widget h-full flex flex-col p-md">
  <div class="flex items-center justify-between gap-sm mb-sm flex-shrink-0">
    <p class="text-label-sm text-outline uppercase tracking-wider truncate">{{ title }}</p>
    {% if payload.view_all_url %}
    <a
      href="{{ payload.view_all_url }}"
      class="text-label-sm text-on-surface-variant hover:text-primary transition-colors inline-flex items-center gap-1 flex-shrink-0 no-underline"
    >
      View all
      <i data-lucide="arrow-up-right" class="w-3.5 h-3.5"></i>
    </a>
    {% endif %}
  </div>
  {% if payload.items and payload.items | length > 0 %}
  {# Divider on each feed item — `surface-container-highest` gives
     a barely-there separator in dark mode without the bluish-white
     bloom `outline-variant` was leaving. Same token as the
     scrollbar thumb so the two visual rails read as one family. #}
  <ul class="flex-1 overflow-y-auto divide-y divide-surface-container-highest">
    {% for item in payload.items %}
    <li class="flex items-start gap-sm py-sm first:pt-0 last:pb-0">
      <div class="w-7 h-7 rounded-full bg-surface-container-high border border-outline-variant/60 flex items-center justify-center flex-shrink-0 mt-0.5">
        <span class="text-[10px] font-semibold text-on-surface-variant uppercase tracking-wide">{{ item.actor[:2] }}</span>
      </div>
      <div class="flex-1 min-w-0">
        {# Username takes `on-surface-variant` (softer) instead of
           `on-surface` (near-white) — pure white feels shouty in
           dark mode and crowds the more important data (the verb
           + relative time). Hover bumps it to full contrast. #}
        <p class="text-body-sm leading-snug">
          <span class="font-medium text-on-surface-variant hover:text-on-surface transition-colors">{{ item.actor }}</span>
          <span class="text-outline"> {{ item.verb }} </span>
          {% if item.object_link %}
          <a href="{{ item.object_link }}" class="text-primary hover:underline">{{ item.object }}</a>
          {% else %}
          <span class="text-on-surface-variant">{{ item.object }}</span>
          {% endif %}
        </p>
        {# Relative time: "2 hours ago" reads at a glance; absolute
           datetime lives in the tooltip for the rare "but exactly
           WHEN?" question. #}
        <time
          class="text-label-sm text-outline/70 tabular-nums"
          title="{{ item.at | humanize_date }}"
        >{{ item.at | naturaltime }}</time>
      </div>
    </li>
    {% endfor %}
  </ul>
  {% else %}
  <p class="text-label-sm text-outline italic mt-auto">No recent activity.</p>
  {% endif %}
</div>
{% endmacro %}