docs: spec video-card state unification — one "Summarize now" verb + honest no-captions state
The card shows two verbs ("Try now" for rate-limited, "Summarize" for pending)
for one user intent, and — the real bug — a no-captions video falls into the
pending branch and wrongly shows a Summarize button that can only fail. Spec
collapses to one quiet "Summarize now" verb wherever a nudge is possible (both
handlers unchanged underneath), adds an honest no-button "No transcript
available" state, and keeps the card status-first (buttons are exceptions in auto
mode). View-layer only.
This commit is contained in:
@@ -0,0 +1,70 @@
|
|||||||
|
# Spec — Unify video-card states: one "Summarize now" verb, honest no-captions state
|
||||||
|
|
||||||
|
**Repo:** tapir · **Size:** small, **view-layer only** (`views.templ` + a little CSS in
|
||||||
|
`view.go`; regenerate `views_templ.go`). No handler, store, or DB change. The two existing
|
||||||
|
handlers (`/summarize`, `/retry-now`) stay exactly as they are — only what the card *shows*
|
||||||
|
changes.
|
||||||
|
|
||||||
|
**Why.** The video card today presents two different verbs — "Try now" (`.btn-retry`, on
|
||||||
|
rate-limited videos) and "Summarize" (`.btn-secondary`, on pending videos) — for what the user
|
||||||
|
experiences as one intent: *"summarize this video now."* The user doesn't know or care about the
|
||||||
|
internal pipeline state (rate-limited vs. manual-queue); two differently-labelled, differently-
|
||||||
|
styled buttons leak that state machine into the UI as a choice. Also: a **no-captions video
|
||||||
|
(`TranscriptStatus == "none"`) currently falls into the `else` branch and wrongly shows a
|
||||||
|
"Summarize" button** that, if clicked, tries and fails — there are no captions to fetch. That's
|
||||||
|
the confusing dead-end to remove.
|
||||||
|
|
||||||
|
In normal use the maintainer is in **auto mode**, so these buttons are *exceptions*, not the main
|
||||||
|
path — videos summarize themselves. So the card should be **status-first**: the state is what the
|
||||||
|
user reads constantly; the manual nudge is a small, quiet affordance for impatience, not a
|
||||||
|
prominent call-to-action.
|
||||||
|
|
||||||
|
## The honest per-state card model (footer of `VideoCard`)
|
||||||
|
|
||||||
|
Restructure the footer branch in `VideoCard` (in `internal/web/views.templ`) to these states.
|
||||||
|
The branch ORDER matters (summarized first, then terminal/no-action states, then actionable):
|
||||||
|
|
||||||
|
1. **Summarized** — preview + provider chip + fallback badge + actions. **No button.** (unchanged)
|
||||||
|
2. **No captions** (`r.TranscriptStatus == "none"`) — **NEW branch.** Quiet status text, e.g.
|
||||||
|
`No transcript available` (use a muted `.card-state`/`.chip-retry`-style treatment, NOT a
|
||||||
|
button). This is a terminal honest dead-end — the user can do nothing, so offer nothing.
|
||||||
|
3. **Queued / requested** (`r.SummarizeRequested`) — "Queued · waiting for the next run". **No
|
||||||
|
button.** (unchanged)
|
||||||
|
4. **Rate-limited** (`r.TranscriptStatus == "rate_limited"`) — quiet status (keep the
|
||||||
|
"fetching soon" sense) + a **quiet "Summarize now"** button POSTing to `retryNowURL` (clears
|
||||||
|
backoff then processes). Same quiet style as state 5.
|
||||||
|
5. **Pending** (else — discovered, not yet attempted) — quiet "Not summarized" + a **quiet
|
||||||
|
"Summarize now"** button POSTing to `summarizeURL` (flips the queue flag then processes).
|
||||||
|
|
||||||
|
## Unify the verb and the style
|
||||||
|
- **One label everywhere a manual nudge is offered: "Summarize now"** (states 4 and 5). Drop the
|
||||||
|
"Try now" wording entirely.
|
||||||
|
- **One quiet style** for both: use the understated `.btn-retry` pattern (small, pill, outline,
|
||||||
|
transparent bg) — NOT `.btn-secondary`/`.btn` (heavier). Rename the CSS class to something
|
||||||
|
state-neutral (e.g. `.btn-quiet` or `.btn-summarize-now`) so it no longer reads as
|
||||||
|
"retry"-specific; keep the same visual. The point: the nudge is subtle, status is primary.
|
||||||
|
- Keep both `<form>`s posting to their respective existing handler URLs
|
||||||
|
(`retryNowURL` for rate-limited, `summarizeURL` for pending) with the existing HTMX
|
||||||
|
attributes (`hx-post`, `hx-target=#video-{id}`, `hx-swap=outerHTML`) — only the button
|
||||||
|
label/class change. The backend side-effect difference (clear-backoff vs. set-flag) stays
|
||||||
|
invisible to the user, which is correct.
|
||||||
|
- Drop the engineer-facing `title="Fetch transcript now through the shared rate gate"` tooltip;
|
||||||
|
if a hint is wanted, make it user-facing ("Summarize this one now").
|
||||||
|
|
||||||
|
## Quietness check (the design intent)
|
||||||
|
The summary content and the per-state *status* are the card's primary information. The "Summarize
|
||||||
|
now" button is a minor affordance. Do not make it a prominent solid-accent CTA — it must read as
|
||||||
|
"you can nudge this if you're impatient", not "action required". Status text uses muted styling;
|
||||||
|
the button uses the quiet outline style.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
- `videocard_internal_test.go` (exists): assert each of the 5 states renders the expected
|
||||||
|
footer — summarized (no button), no-captions (status, NO button, no `summarize`/`retry-now`
|
||||||
|
URL present), queued (no button), rate-limited ("Summarize now" → retry-now URL), pending
|
||||||
|
("Summarize now" → summarize URL). The key new assertion: **a `none`-status video renders no
|
||||||
|
action button and no POST URL.**
|
||||||
|
- Assert the label string "Try now" no longer appears anywhere in rendered output.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
Handler/DB changes; the detail-page action buttons (watched/skipped/saved — unrelated); the
|
||||||
|
pipeline stats bar wording; auto/manual mode behaviour. Verb/label/style/no-captions-state only.
|
||||||
Reference in New Issue
Block a user