package web import ( "strings" "gitea.d-ma.be/mathias/tapir/internal/adapters/store" ) // Layout is the shared HTML shell. HTMX drives the progressive interactions // (filters, action toggles); every interaction also degrades to a plain form // POST/GET when JS is absent (ui-spec.md §4). templ Layout(title string) { { title } @templ.Raw(styleTag)
Tapir
{ children... }
} // ListPage is the full summary list with the filter form. HTMX swaps only the // #summary-list region; a non-HTMX request renders the whole page. templ ListPage(rows []store.SummaryRow, f Filter) { @Layout("Tapir — Summaries") { @filterForm(f)
@summaryTable(rows)
} } templ filterForm(f Filter) {
} // summaryTable is the swappable list fragment: title · channel · published · // provider · fallback badge · current action state. templ summaryTable(rows []store.SummaryRow) { if len(rows) == 0 { } for _, r := range rows { }
Title Channel Published Provider Actions
No summaries.
{ displayTitle(r) } { r.Channel } { displayDate(r.PublishedAt) } { r.AIProvider } if r.FallbackUsed { fallback } if len(r.Actions) == 0 { } else { { strings.Join(r.Actions, ", ") } }
} // DetailPage is the full summary view: text, highlights, takeaways, metadata, // and the action button group. templ DetailPage(r store.SummaryRow) { @Layout("Tapir — " + displayTitle(r)) {

{ displayTitle(r) }

{ r.Channel } · { displayDate(r.PublishedAt) } · { r.AIProvider } if r.AIModel != "" { { "(" + r.AIModel + ")" } } if r.FallbackUsed { fallback }

if r.URL != "" {

watch on source ↗

} @ActionButtons(r.VideoID, actionSet(r.Actions))

Summary

{ r.Summary }

if len(r.Highlights) > 0 {

Highlights

} if len(r.Takeaways) > 0 {

Takeaways

}
} } // ActionButtons is the toggle group fragment returned by POST /v/{id}/action. // Each button submits its verb; HTMX swaps this element in place (outerHTML), // and without JS the form POSTs and the handler redirects back to the detail // page. active marks the verbs currently set for (user, video). templ ActionButtons(videoID string, active map[string]bool) {
for _, v := range actionVerbs { }
}