feat(web): summarization-mode UI — all-videos list, Summarize queue, mode toggle
The list now shows ALL videos (ListVideos), not just summaries. Summarized cards
are unchanged; discovered-but-unsummarized videos render with a muted "pending"
treatment and either a "Summarize" button or a "Queued" chip.
- POST /v/{videoId}/summarize queues a video (RequestSummarize) and returns the
refreshed card — it does NOT run the engine inline; `tapir run` is the single
summarization driver, which picks up the flag on its next pass.
- Account page gains an Automatic/Manual toggle (POST /account/summarize-mode →
SetAutoSummarize), shown as the current mode with a one-click switch.
- Both new POSTs degrade without JS (redirect back); HTMX swaps the fragment.
VideoCard and summarizeModeControl are extracted templ fragments reused as the
HTMX swap targets. views_templ.go regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -171,6 +171,36 @@ func actionURL(videoID string) templ.SafeURL {
|
||||
return templ.SafeURL("/v/" + videoID + "/action")
|
||||
}
|
||||
|
||||
// summarizeURL builds the manual-queue POST path for a video id.
|
||||
func summarizeURL(videoID string) templ.SafeURL {
|
||||
return templ.SafeURL("/v/" + videoID + "/summarize")
|
||||
}
|
||||
|
||||
// summarizeModeLabel names the current mode for display.
|
||||
func summarizeModeLabel(auto bool) string {
|
||||
if auto {
|
||||
return "Automatic"
|
||||
}
|
||||
return "Manual"
|
||||
}
|
||||
|
||||
// summarizeModeToggleLabel is the caption on the toggle button — it names the mode
|
||||
// the click switches TO (the opposite of the current one).
|
||||
func summarizeModeToggleLabel(auto bool) string {
|
||||
if auto {
|
||||
return "Switch to manual"
|
||||
}
|
||||
return "Switch to automatic"
|
||||
}
|
||||
|
||||
// boolStr renders a bool as the "enabled" form value the toggle submits.
|
||||
func boolStr(b bool) string {
|
||||
if b {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
// externalURL passes a stored source URL through templ's URL sanitiser.
|
||||
func externalURL(u string) templ.SafeURL {
|
||||
return templ.URL(u)
|
||||
@@ -347,6 +377,15 @@ main { max-width: 60rem; margin: 0 auto; padding: var(--s4) var(--s3); }
|
||||
.card-state { color: var(--muted); font-size: .8rem; }
|
||||
.badge { display: inline-block; padding: .15rem .55rem; border-radius: 999px; background: var(--badge-bg); color: var(--badge-fg); font-size: .72rem; font-weight: 600; }
|
||||
|
||||
/* pending (discovered-but-unsummarized) card: muted until summarized */
|
||||
.card-pending { border-style: dashed; }
|
||||
.card-pending .card-title { color: var(--muted); font-weight: 600; }
|
||||
|
||||
/* summarization mode toggle on the account page */
|
||||
.summarize-mode { display: flex; gap: var(--s3); align-items: center; flex-wrap: wrap; }
|
||||
.summarize-mode p { margin: 0; }
|
||||
.summarize-mode form { margin: 0; }
|
||||
|
||||
/* empty state */
|
||||
.empty { text-align: center; color: var(--muted); padding: var(--s5) var(--s4); border: 1px dashed var(--line); border-radius: var(--radius); background: var(--card); }
|
||||
.empty strong { display: block; color: var(--fg); font-size: 1.05rem; margin-bottom: var(--s2); }
|
||||
|
||||
Reference in New Issue
Block a user