From 40b703e02a5c4c320992cbd2d7926f6c2c54d981 Mon Sep 17 00:00:00 2001 From: Mathias Date: Mon, 8 Jun 2026 13:55:07 +0200 Subject: [PATCH] feat(web): collapse older + caption-less videos in the list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop the un-summarized back-catalogue from burying the readable summaries (UX review B3/B4). One feed, with a noise-collapse — not sections: - Summarized + recent un-summarized videos lead inline as cards. - Un-summarized videos older than the recency window collapse into a single "Show N older videos — summarize on demand" disclosure (they will not auto-fill; they are manual-only). Window comes from App.RecencyWindow (= cfg.AutoSummarizeWindow); 0 disables the collapse (all inline). - Caption-less videos collapse into one honest line ("N videos have no captions and can't be summarized") instead of N dead terminal cards. bucketRows is a pure classifier (cutoff-driven; undated rows never age out); App gains RecencyWindow + an injectable clock for the cutoff. Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/tapir/main.go | 2 +- internal/web/handlers.go | 39 +- internal/web/handlers_test.go | 42 ++ internal/web/view.go | 60 ++ internal/web/views.templ | 32 +- internal/web/views_templ.go | 1119 +++++++++++++++++---------------- 6 files changed, 748 insertions(+), 546 deletions(-) diff --git a/cmd/tapir/main.go b/cmd/tapir/main.go index 14dabf2..7dc2716 100644 --- a/cmd/tapir/main.go +++ b/cmd/tapir/main.go @@ -193,7 +193,7 @@ func cmdServe(ctx context.Context, log *slog.Logger) error { // The file-backed SecretStore is shared by the connect flow (writes tokens) // and account management (deletes them on disconnect / delete-account). secretStore := secrets.NewFileStore(cfg.SecretsFile) - app := &web.App{Store: st, Identity: st, Auth: authn, Secrets: secretStore, Log: log} + app := &web.App{Store: st, Identity: st, Auth: authn, Secrets: secretStore, Log: log, RecencyWindow: cfg.AutoSummarizeWindow} // User onboarding is handled by the IdP (Authentik invite flow), not Tapir — // the Dex local-password provisioning path was removed (ADR-019). An diff --git a/internal/web/handlers.go b/internal/web/handlers.go index 234cc20..0ba3762 100644 --- a/internal/web/handlers.go +++ b/internal/web/handlers.go @@ -5,6 +5,7 @@ import ( "errors" "log/slog" "net/http" + "time" "github.com/a-h/templ" @@ -80,6 +81,32 @@ type App struct { // Processing tracks in-flight immediate summarizations so the status endpoint // shows the animation until the summary lands. The zero value is ready to use. Processing ProcessingSet + // RecencyWindow mirrors the auto-summarize recency bound: un-summarized videos + // published before now-RecencyWindow collapse into the "older videos" + // disclosure on the list, so the readable summaries are not buried (B3). Zero + // disables the collapse (everything stays inline). + RecencyWindow time.Duration + // Now is an injectable clock for the recency cutoff (tests fix it). Nil = + // time.Now. + Now func() time.Time +} + +// now returns the App's clock (time.Now unless overridden for tests). +func (a *App) now() time.Time { + if a.Now != nil { + return a.Now() + } + return time.Now() +} + +// recencyCutoff is the timestamp before which an un-summarized video counts as +// "older" and collapses into the disclosure. A zero RecencyWindow yields the zero +// time, which bucketRows treats as "collapse disabled". +func (a *App) recencyCutoff() time.Time { + if a.RecencyWindow <= 0 { + return time.Time{} + } + return a.now().Add(-a.RecencyWindow) } func (a *App) logger() *slog.Logger { @@ -169,12 +196,14 @@ func (a *App) handleList(w http.ResponseWriter, r *http.Request) { } stats := pipelineStats(allRows) rows := f.apply(allRows) + buckets := bucketRows(rows, a.recencyCutoff()) // hasConnected drives the empty state: a fresh account with a connection but - // no `tapir run` yet has zero rows, and we want it to read "connected, run - // tapir" rather than "nothing here". Only needed when the list is empty. + // no discovery pass yet has zero rows, and we want it to read "connected, + // summaries land gradually" rather than "nothing here". Only needed when the + // list is empty. hasConnected := false - if len(rows) == 0 { + if buckets.empty() { conns, err := a.Store.ConnectionsForUser(r.Context(), userID) if err != nil { a.serverError(w, r, "connections for user", err) @@ -184,10 +213,10 @@ func (a *App) handleList(w http.ResponseWriter, r *http.Request) { } if isHTMX(r) { - a.render(w, r, summaryList(rows, hasConnected)) + a.render(w, r, summaryList(buckets, hasConnected)) return } - a.render(w, r, ListPage(rows, f, stats, takeFlash(w, r), hasConnected)) + a.render(w, r, ListPage(buckets, f, stats, takeFlash(w, r), hasConnected)) } // handleDetail renders one summary in full (highlights, takeaways, action group). diff --git a/internal/web/handlers_test.go b/internal/web/handlers_test.go index fd302cf..9d252ab 100644 --- a/internal/web/handlers_test.go +++ b/internal/web/handlers_test.go @@ -318,6 +318,48 @@ func TestListShowsSummarizeButtonForUnsummarized(t *testing.T) { require.NotContains(t, html, "Queued", "not queued yet") } +// TestListCollapsesOlderAndNoCaption verifies the feed IA (UX review B3/B4): +// summarized + recent un-summarized cards lead inline; older un-summarized +// videos collapse into a single disclosure; caption-less videos collapse into a +// one-line count instead of dead cards. +func TestListCollapsesOlderAndNoCaption(t *testing.T) { + ctx := context.Background() + app := newApp(t) + now := time.Date(2026, 6, 8, 12, 0, 0, 0, time.UTC) + app.RecencyWindow = 7 * 24 * time.Hour + app.Now = func() time.Time { return now } + p := rawPool(t) + resetDB(t, p) + + const ( + vRecent = "cccccccc-cccc-cccc-cccc-cccccccccccc" // 2d old, unsummarized → inline + vOld = "dddddddd-dddd-dddd-dddd-dddddddddddd" // 30d old, unsummarized → disclosure + vNoCap = "eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee" // caption-less → collapsed line + ) + require.NoError(t, deliver(ctx, app, videoX, "body x")) // summarized, recent + seedVideo(t, p, videoX, "Summarized X", "https://x", now.Add(-24*time.Hour)) + seedVideo(t, p, vRecent, "Recent Pending", "https://r", now.Add(-2*24*time.Hour)) + seedVideo(t, p, vOld, "Old Pending", "https://o", now.Add(-30*24*time.Hour)) + seedVideo(t, p, vNoCap, "No Caption Vid", "https://n", now.Add(-40*24*time.Hour)) + _, err := p.Exec(ctx, `UPDATE videos SET transcript_status = 'none' WHERE id = $1`, vNoCap) + require.NoError(t, err) + + rec := do(t, app, httptest.NewRequest(http.MethodGet, "/", nil)) + require.Equal(t, http.StatusOK, rec.Code) + html := body(t, rec) + + disclosure := strings.Index(html, "Show 1 older videos") + require.GreaterOrEqual(t, disclosure, 0, "older-videos disclosure present") + // Summarized + recent un-summarized lead inline, above the disclosure. + require.Less(t, strings.Index(html, "Summarized X"), disclosure, "summarized card is inline") + require.Less(t, strings.Index(html, "Recent Pending"), disclosure, "recent pending is inline") + // The older video is hidden inside the disclosure, after its summary. + require.Greater(t, strings.Index(html, "Old Pending"), disclosure, "older video lives in the disclosure") + // Caption-less video is a one-line count, never a card. + require.Contains(t, html, "have no captions") + require.NotContains(t, html, "No Caption Vid", "caption-less video is collapsed, not a card") +} + func TestRequestSummarizeQueuesAndRendersCard(t *testing.T) { ctx := context.Background() app := newApp(t) diff --git a/internal/web/view.go b/internal/web/view.go index 79103bc..fcd6f36 100644 --- a/internal/web/view.go +++ b/internal/web/view.go @@ -414,6 +414,57 @@ func retryNowURL(videoID string) templ.SafeURL { return templ.SafeURL("/v/" + videoID + "/retry-now") } +// listBuckets splits the (already filtered) video list into what the list view +// shows where, so the readable summaries are not buried under the un-summarized +// back-catalogue (UX review B3/B4). It is one feed with a noise-collapse, not +// separate sections: +// - Main: summarized videos + recent un-summarized ones — shown inline as cards. +// - Older: un-summarized videos published before the recency cutoff — collapsed +// behind a single "Show N older videos" disclosure (they will not auto-fill; +// they are summarize-on-demand). +// - NoCaption: count of un-summarized videos with no caption track — collapsed +// to one honest line instead of N dead terminal cards. +type listBuckets struct { + Main []store.SummaryRow + Older []store.SummaryRow + NoCaption int +} + +// bucketRows classifies rows into the list buckets given a recency cutoff. A zero +// cutoff (recency collapse disabled) leaves Older empty — every un-summarized, +// captioned video stays inline. Order within each bucket is preserved. +func bucketRows(rows []store.SummaryRow, cutoff time.Time) listBuckets { + var b listBuckets + for _, r := range rows { + switch { + case r.Summarized: + b.Main = append(b.Main, r) + case r.TranscriptStatus == "none": + b.NoCaption++ + case isOlder(r, cutoff): + b.Older = append(b.Older, r) + default: + b.Main = append(b.Main, r) + } + } + return b +} + +// isOlder reports whether an un-summarized row falls before the recency cutoff. +// A zero cutoff (window disabled) or an undated row is never "older" — it cannot +// be aged out, so it stays inline rather than being hidden in the disclosure. +func isOlder(r store.SummaryRow, cutoff time.Time) bool { + if cutoff.IsZero() || r.PublishedAt.IsZero() { + return false + } + return r.PublishedAt.Before(cutoff) +} + +// empty reports whether there is nothing to show at all (drives the empty state). +func (b listBuckets) empty() bool { + return len(b.Main) == 0 && len(b.Older) == 0 && b.NoCaption == 0 +} + // Filter holds the list-view query parameters. Empty fields mean "no constraint". // Dates are kept as the raw YYYY-MM-DD strings so the form re-renders the user's // input verbatim; parsing happens in matchFilter. @@ -539,6 +590,15 @@ a.btn, a.btn:visited { color: var(--accent-fg); } .pipeline-bar span { display: flex; align-items: center; gap: var(--s1); } .pipeline-bar span + span::before { content: "·"; margin-right: var(--s1); } .pipeline-note { margin: calc(-1 * var(--s2)) 0 var(--s3); font-size: .8rem; line-height: 1.5; max-width: 40rem; } +/* one-line count of caption-less videos (collapsed instead of N dead cards) */ +.list-note { margin: var(--s3) 0 0; font-size: .85rem; } +/* older un-summarized back-catalogue, collapsed behind a disclosure so it does + not bury the readable summaries above it */ +.older-videos { margin-top: var(--s4); } +.older-videos > summary { cursor: pointer; font-size: .85rem; font-weight: 600; color: var(--accent); padding: var(--s2) 0; list-style: revert; } +.older-videos > summary:hover { text-decoration: underline; } +.older-videos[open] > summary { margin-bottom: var(--s3); } +.older-videos .cards { margin-top: 0; } .card-nudge-form { display: inline; } .btn-quiet { font: inherit; font-size: .72rem; font-weight: 600; padding: .15rem .55rem; border-radius: 999px; border: 1px solid var(--accent); background: transparent; color: var(--accent); cursor: pointer; } .btn-quiet:hover { background: var(--accent-weak); } diff --git a/internal/web/views.templ b/internal/web/views.templ index 398c3bb..849ef33 100644 --- a/internal/web/views.templ +++ b/internal/web/views.templ @@ -104,7 +104,7 @@ templ flashBanner(code string) { // #summary-list region; a non-HTMX request renders the whole page. flash carries // a one-shot notification (e.g. "connected", "registered") surfaced on arrival // after a POST→redirect. -templ ListPage(rows []store.SummaryRow, f Filter, stats PipelineStats, flash string, hasConnected bool) { +templ ListPage(b listBuckets, f Filter, stats PipelineStats, flash string, hasConnected bool) { @Layout("Tapir — Summaries") { @flashBanner(flash) @filterForm(f) @@ -118,7 +118,7 @@ templ ListPage(rows []store.SummaryRow, f Filter, stats PipelineStats, flash str

}
- @summaryList(rows, hasConnected) + @summaryList(b, hasConnected)
} } @@ -163,11 +163,14 @@ templ filterForm(f Filter) { } -// summaryList is the swappable list fragment: one card per video (summarized or -// not). Cards reflow to a single column on mobile; an empty list shows a friendly -// first-run state instead of a blank table. -templ summaryList(rows []store.SummaryRow, hasConnected bool) { - if len(rows) == 0 { +// summaryList is the swappable list fragment. It leads with readable summaries + +// recent un-summarized cards (b.Main), then collapses the noise so it does not +// bury the payload (UX review B3/B4): a one-line count of caption-less videos, +// and a single disclosure holding the older un-summarized back-catalogue. Cards +// reflow to a single column on mobile; an empty list shows a friendly first-run +// state instead of a blank table. +templ summaryList(b listBuckets, hasConnected bool) { + if b.empty() { if hasConnected {
Your account is connected @@ -182,10 +185,23 @@ templ summaryList(rows []store.SummaryRow, hasConnected bool) { } } else {
    - for _, r := range rows { + for _, r := range b.Main { @VideoCard(r) }
+ if b.NoCaption > 0 { +

{ fmt.Sprintf("%d video(s) have no captions and can't be summarized.", b.NoCaption) }

+ } + if len(b.Older) > 0 { +
+ { fmt.Sprintf("Show %d older videos — summarize on demand", len(b.Older)) } +
    + for _, r := range b.Older { + @VideoCard(r) + } +
+
+ } } } diff --git a/internal/web/views_templ.go b/internal/web/views_templ.go index ec7134f..3892195 100644 --- a/internal/web/views_templ.go +++ b/internal/web/views_templ.go @@ -304,7 +304,7 @@ func flashBanner(code string) templ.Component { // #summary-list region; a non-HTMX request renders the whole page. flash carries // a one-shot notification (e.g. "connected", "registered") surfaced on arrival // after a POST→redirect. -func ListPage(rows []store.SummaryRow, f Filter, stats PipelineStats, flash string, hasConnected bool) templ.Component { +func ListPage(b listBuckets, f Filter, stats PipelineStats, flash string, hasConnected bool) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -373,7 +373,7 @@ func ListPage(rows []store.SummaryRow, f Filter, stats PipelineStats, flash stri if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = summaryList(rows, hasConnected).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = summaryList(b, hasConnected).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -563,10 +563,13 @@ func filterForm(f Filter) templ.Component { }) } -// summaryList is the swappable list fragment: one card per video (summarized or -// not). Cards reflow to a single column on mobile; an empty list shows a friendly -// first-run state instead of a blank table. -func summaryList(rows []store.SummaryRow, hasConnected bool) templ.Component { +// summaryList is the swappable list fragment. It leads with readable summaries + +// recent un-summarized cards (b.Main), then collapses the noise so it does not +// bury the payload (UX review B3/B4): a one-line count of caption-less videos, +// and a single disclosure holding the older un-summarized back-catalogue. Cards +// reflow to a single column on mobile; an empty list shows a friendly first-run +// state instead of a blank table. +func summaryList(b listBuckets, hasConnected bool) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { @@ -587,7 +590,7 @@ func summaryList(rows []store.SummaryRow, hasConnected bool) templ.Component { templ_7745c5c3_Var22 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - if len(rows) == 0 { + if b.empty() { if hasConnected { templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
Your account is connected Tapir is finding your subscriptions and fetching captions — summaries appear here gradually. Check back later.
") if templ_7745c5c3_Err != nil { @@ -604,7 +607,7 @@ func summaryList(rows []store.SummaryRow, hasConnected bool) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - for _, r := range rows { + for _, r := range b.Main { templ_7745c5c3_Err = VideoCard(r).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -614,6 +617,58 @@ func summaryList(rows []store.SummaryRow, hasConnected bool) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + if b.NoCaption > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var23 string + templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d video(s) have no captions and can't be summarized.", b.NoCaption)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 193, Col: 113} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(b.Older) > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var24 string + templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("Show %d older videos — summarize on demand", len(b.Older))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 197, Col: 88} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, r := range b.Older { + templ_7745c5c3_Err = VideoCard(r).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } } return nil }) @@ -645,297 +700,297 @@ func VideoCard(r store.SummaryRow) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var23 := templ.GetChildren(ctx) - if templ_7745c5c3_Var23 == nil { - templ_7745c5c3_Var23 = templ.NopComponent + templ_7745c5c3_Var25 := templ.GetChildren(ctx) + if templ_7745c5c3_Var25 == nil { + templ_7745c5c3_Var25 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - var templ_7745c5c3_Var24 = []any{"card", templ.KV("card-pending", !r.Summarized)} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var24...) + var templ_7745c5c3_Var26 = []any{"card", templ.KV("card-pending", !r.Summarized)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var26...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if r.Summarized { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var29 string - templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 206, Col: 44} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 220, Col: 56} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - if cardMeta(r) != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var30 string - templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(cardMeta(r)) + templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 209, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 220, Col: 76} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
  • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var31 string + templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 222, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + if cardMeta(r) != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var32 string + templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(cardMeta(r)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 225, Col: 39} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if r.Summarized { if p := previewText(r.Summary, 160); p != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var31 string - templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(p) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 213, Col: 33} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if r.Summarized { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if r.AIProvider != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var32 string - templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(r.AIProvider) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 220, Col: 38} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if r.FallbackUsed { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "fallback") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if len(r.Actions) > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var33 string - templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(r.Actions, ", ")) + templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(p) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 226, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 229, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if r.Summarized { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if r.AIProvider != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var34 string + templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(r.AIProvider) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 236, Col: 38} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if r.FallbackUsed { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "fallback") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(r.Actions) > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var35 string + templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(strings.Join(r.Actions, ", ")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 242, Col: 61} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } } else if r.TranscriptStatus == "none" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, " No transcript available") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " No transcript available") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else if r.SummarizeRequested { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, " Queued summarizing shortly") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " Queued summarizing shortly") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else if r.TranscriptStatus == "rate_limited" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, " In queue
    In queue
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " Not summarized
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var39 string - templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue("#video-" + r.VideoID) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 256, Col: 38} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39) + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " Not summarized
    ") + var templ_7745c5c3_Var39 templ.SafeURL + templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinURLErrs(summarizeURL(r.VideoID)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 270, Col: 37} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\" hx-post=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var40 string + templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.ResolveAttributeValue(string(summarizeURL(r.VideoID))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 271, Col: 46} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var40) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "\" hx-target=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var41 string + templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.ResolveAttributeValue("#video-" + r.VideoID) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 272, Col: 38} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var41) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\" hx-swap=\"outerHTML\" class=\"card-nudge-form\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -964,12 +1019,12 @@ func TapirSpinner() templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var40 := templ.GetChildren(ctx) - if templ_7745c5c3_Var40 == nil { - templ_7745c5c3_Var40 = templ.NopComponent + templ_7745c5c3_Var42 := templ.GetChildren(ctx) + if templ_7745c5c3_Var42 == nil { + templ_7745c5c3_Var42 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "
    ")
    +		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "
    ")
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    @@ -977,7 +1032,7 @@ func TapirSpinner() templ.Component {
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    -		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "
    ")
    +		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "
    ")
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    @@ -985,7 +1040,7 @@ func TapirSpinner() templ.Component {
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    -		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "
    ")
    +		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "
    ")
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    @@ -993,33 +1048,33 @@ func TapirSpinner() templ.Component {
     		if templ_7745c5c3_Err != nil {
     			return templ_7745c5c3_Err
     		}
    -		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var42 string - templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(tapirBarFill) + var templ_7745c5c3_Var44 string + templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(tapirBarFill) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 277, Col: 99} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 293, Col: 99} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "

    Summarizing…

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "

    Summarizing…

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1048,69 +1103,69 @@ func processingCard(r store.SummaryRow) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var43 := templ.GetChildren(ctx) - if templ_7745c5c3_Var43 == nil { - templ_7745c5c3_Var43 = templ.NopComponent + templ_7745c5c3_Var45 := templ.GetChildren(ctx) + if templ_7745c5c3_Var45 == nil { + templ_7745c5c3_Var45 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\" hx-get=\"") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var47 string + templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.ResolveAttributeValue(string(statusURL(r.VideoID))) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 307, Col: 39} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var47) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "\" hx-trigger=\"every 2s\" hx-swap=\"outerHTML\">
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var48 string + templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 311, Col: 43} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if cardMeta(r) != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var47 string - templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(cardMeta(r)) + var templ_7745c5c3_Var49 string + templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(cardMeta(r)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 297, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 313, Col: 39} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1119,7 +1174,7 @@ func processingCard(r store.SummaryRow) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "
  • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1145,12 +1200,12 @@ func DetailPage(r store.SummaryRow) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var48 := templ.GetChildren(ctx) - if templ_7745c5c3_Var48 == nil { - templ_7745c5c3_Var48 = templ.NopComponent + templ_7745c5c3_Var50 := templ.GetChildren(ctx) + if templ_7745c5c3_Var50 == nil { + templ_7745c5c3_Var50 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var49 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var51 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -1162,99 +1217,99 @@ func DetailPage(r store.SummaryRow) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var50 string - templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) + var templ_7745c5c3_Var52 string + templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.JoinStringErrs(displayTitle(r)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 308, Col: 24} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 324, Col: 24} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var52)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if detailMeta(r) != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var51 string - templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(detailMeta(r)) + var templ_7745c5c3_Var53 string + templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(detailMeta(r)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 311, Col: 26} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 327, Col: 26} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if r.FallbackUsed { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, "fallback") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "fallback") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if url, ok := embedURL(r.ProviderVideoID); ok { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "\" loading=\"lazy\" referrerpolicy=\"strict-origin-when-cross-origin\" allow=\"accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" allowfullscreen>") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if r.URL != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "

    watch on source ↗

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "\" rel=\"noopener noreferrer\">watch on source ↗

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1264,83 +1319,83 @@ func DetailPage(r store.SummaryRow) templ.Component { return templ_7745c5c3_Err } if len(r.Takeaways) > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "

    Takeaways

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "

      Takeaways

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } for _, t := range r.Takeaways { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "
      • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "
      • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var55 string - templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(t) + var templ_7745c5c3_Var57 string + templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(t) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 342, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 358, Col: 14} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "
      • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "
      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if len(r.Highlights) > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "

    Highlights

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "

      Highlights

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } for _, h := range r.Highlights { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "
      • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "
      • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var56 string - templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(h) + var templ_7745c5c3_Var58 string + templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(h) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 352, Col: 14} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 368, Col: 14} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "
      • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "
      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, "

    Summary

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, "

    Summary

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var57 string - templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(r.Summary) + var templ_7745c5c3_Var59 string + templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(r.Summary) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 359, Col: 31} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 375, Col: 31} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = Layout("Tapir — "+displayTitle(r)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var49), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout("Tapir — "+displayTitle(r)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var51), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1367,12 +1422,12 @@ func RegisterPage(email, errMsg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var58 := templ.GetChildren(ctx) - if templ_7745c5c3_Var58 == nil { - templ_7745c5c3_Var58 = templ.NopComponent + templ_7745c5c3_Var60 := templ.GetChildren(ctx) + if templ_7745c5c3_Var60 == nil { + templ_7745c5c3_Var60 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var59 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var61 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -1384,59 +1439,59 @@ func RegisterPage(email, errMsg string) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "

    Complete your registration

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "

    Complete your registration

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if email != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "

    Signed in as ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 117, "

    Signed in as ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var60 string - templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.JoinStringErrs(email) + var templ_7745c5c3_Var62 string + templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.JoinStringErrs(email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 373, Col: 40} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 389, Col: 40} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var60)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var62)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, ".

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 118, ".

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "

    Choose a display name to finish setting up your Tapir account.

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 119, "

    Choose a display name to finish setting up your Tapir account.

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if errMsg != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var61 string - templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(errMsg) + var templ_7745c5c3_Var63 string + templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(errMsg) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 377, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 393, Col: 42} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 115, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 116, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = Layout("Tapir — Register").Render(templ.WithChildren(ctx, templ_7745c5c3_Var59), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout("Tapir — Register").Render(templ.WithChildren(ctx, templ_7745c5c3_Var61), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1464,12 +1519,12 @@ func AccountPage(displayName, email string, conns []store.Connection, autoSummar }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var62 := templ.GetChildren(ctx) - if templ_7745c5c3_Var62 == nil { - templ_7745c5c3_Var62 = templ.NopComponent + templ_7745c5c3_Var64 := templ.GetChildren(ctx) + if templ_7745c5c3_Var64 == nil { + templ_7745c5c3_Var64 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var63 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var65 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -1485,43 +1540,43 @@ func AccountPage(displayName, email string, conns []store.Connection, autoSummar if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 117, "

    Account

    Display name
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 123, "

    Account

    Display name
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var64 string - templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinStringErrs(displayNameOr(displayName)) + var templ_7745c5c3_Var66 string + templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(displayNameOr(displayName)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 405, Col: 36} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 421, Col: 36} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 118, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if email != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 119, "
    Signed in as
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 125, "
    Signed in as
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var65 string - templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(email) + var templ_7745c5c3_Var67 string + templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 408, Col: 16} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 424, Col: 16} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 120, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 126, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 121, "

    Summarization

    Automatic summarizes new videos from about the last week as they are discovered. Older videos stay browsable — summarize them on demand. Manual lets you pick which videos to summarize — every new video appears in your list with a Summarize button.

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "

    Summarization

    Automatic summarizes new videos from about the last week as they are discovered. Older videos stay browsable — summarize them on demand. Manual lets you pick which videos to summarize — every new video appears in your list with a Summarize button.

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1529,178 +1584,178 @@ func AccountPage(displayName, email string, conns []store.Connection, autoSummar if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 122, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "
    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if len(channelErrors) > 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 123, "

    Unavailable channels

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 129, "

    Unavailable channels

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var66 string - templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d channel(s) returned errors on the last discovery pass.", len(channelErrors))) + var templ_7745c5c3_Var68 string + templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d channel(s) returned errors on the last discovery pass.", len(channelErrors))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 425, Col: 100} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 441, Col: 100} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 124, " These may have been deleted or made private on YouTube.

      ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 130, " These may have been deleted or made private on YouTube.

        ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } for _, ce := range channelErrors { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 125, "
      • ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var67 string - templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.JoinStringErrs(ce.ChannelName) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 431, Col: 57} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var67)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 126, " unavailable since ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var68 string - templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(ce.FirstSeen.Format("2006-01-02")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 433, Col: 89} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 127, "
      • ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 128, "
    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 129, "

    Connected accounts

    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - if len(conns) == 0 { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 130, "

    No connected video accounts yet.

    ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - } else { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 131, "
      ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - for _, c := range conns { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 132, "
    • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 131, "
    • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var69 string - templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(providerLabel(c.Provider)) + templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.JoinStringErrs(ce.ChannelName) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 448, Col: 64} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 447, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var69)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 133, " ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 132, " unavailable since ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - if c.ProviderAccount != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 134, "") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var70 string - templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(c.ProviderAccount) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 450, Col: 49} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 135, " ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } + var templ_7745c5c3_Var70 string + templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.JoinStringErrs(ce.FirstSeen.Format("2006-01-02")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 449, Col: 89} } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 136, "") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var70)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 133, "
    • ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 134, "
    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 135, "

    Connected accounts

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(conns) == 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 136, "

    No connected video accounts yet.

    ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 137, "
      ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, c := range conns { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 138, "
    • ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var71 string - templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(c.Status) + templ_7745c5c3_Var71, templ_7745c5c3_Err = templ.JoinStringErrs(providerLabel(c.Provider)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 452, Col: 38} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 464, Col: 64} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var71)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 137, "
      connected ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 139, " ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var72 string - templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.JoinStringErrs(c.ConnectedAt.Format("2006-01-02")) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 454, Col: 83} + if c.ProviderAccount != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 140, "") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var72 string + templ_7745c5c3_Var72, templ_7745c5c3_Err = templ.JoinStringErrs(c.ProviderAccount) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 466, Col: 49} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var72)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 141, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var72)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 142, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 138, "
    • ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 143, "
      connected ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var74 string + templ_7745c5c3_Var74, templ_7745c5c3_Err = templ.JoinStringErrs(c.ConnectedAt.Format("2006-01-02")) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 470, Col: 83} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var74)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 144, "
      ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 140, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 146, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } if !hasYouTube(conns) { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 141, "

    Connect YouTube

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 147, "

    Connect YouTube

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 142, "

    Delete account

    Permanently remove your Tapir account and all of its data — summaries, watch/skip/save actions, and connected accounts. This cannot be undone.

    Delete account…

    This permanently deletes your account and all data. Are you sure?

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 148, "

    Delete account

    Permanently remove your Tapir account and all of its data — summaries, watch/skip/save actions, and connected accounts. This cannot be undone.

    Delete account…

    This permanently deletes your account and all data. Are you sure?

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = Layout("Tapir — Account").Render(templ.WithChildren(ctx, templ_7745c5c3_Var63), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = Layout("Tapir — Account").Render(templ.WithChildren(ctx, templ_7745c5c3_Var65), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1728,51 +1783,51 @@ func summarizeModeControl(auto bool) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var74 := templ.GetChildren(ctx) - if templ_7745c5c3_Var74 == nil { - templ_7745c5c3_Var74 = templ.NopComponent + templ_7745c5c3_Var76 := templ.GetChildren(ctx) + if templ_7745c5c3_Var76 == nil { + templ_7745c5c3_Var76 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 143, "

    Current mode: ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - var templ_7745c5c3_Var75 string - templ_7745c5c3_Var75, templ_7745c5c3_Err = templ.JoinStringErrs(summarizeModeLabel(auto)) - if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/views.templ`, Line: 492, Col: 53} - } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var75)) - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 144, "

    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 150, "

    ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -1800,117 +1855,117 @@ func ActionButtons(videoID string, active map[string]bool) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var78 := templ.GetChildren(ctx) - if templ_7745c5c3_Var78 == nil { - templ_7745c5c3_Var78 = templ.NopComponent + templ_7745c5c3_Var80 := templ.GetChildren(ctx) + if templ_7745c5c3_Var80 == nil { + templ_7745c5c3_Var80 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 147, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, "\" hx-target=\"#action-buttons\" hx-swap=\"outerHTML\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } for _, v := range actionVerbs { - var templ_7745c5c3_Var81 = []any{"action", templ.KV("active", active[v])} - templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var81...) + var templ_7745c5c3_Var83 = []any{"action", templ.KV("active", active[v])} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var83...) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 150, "") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 160, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 155, "
    ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 161, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }