feat(web): honest sparse-state and queue copy

Make the sparse reality legible instead of implying abundance or imminence
(UX review A1-A4, A6):

- Empty-connected state drops the impossible "Run `tapir run`" instruction
  (no shell for web users; discovery is in-process since ADR-018) for a
  passive "summaries appear gradually, check back later".
- Pipeline bar reframes counts by what the user can do: "N ready · M in queue
  · K no captions" (was "summarized / fetching soon / pending").
- A one-line note explains captions are fetched slowly on purpose to respect
  YouTube's limits — turning confusing emptiness into intentional design.
- Card state for throttled videos reads "In queue", not "Fetching soon…"
  (256 items behind a per-IP gate are not all imminent — ADR-014).
- Quiet nudge button drops the over-promising "now": "Summarize", not
  "Summarize now". On click the card still honestly becomes "Queued".
- Queued card says "summarizing shortly", not "waiting for the next run"
  (no scheduler jargon).

Pure copy/label — no logic, DB, or fetch-rate change. The rate gate is
untouched; scarcity is surfaced, never engineered around.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 13:42:40 +02:00
co-authored by Claude Opus 4.8
parent 9c7e3be984
commit a1a5217d77
4 changed files with 326 additions and 322 deletions
+10 -10
View File
@@ -46,7 +46,7 @@ func TestVideoCard_State1_Summarized(t *testing.T) {
t.Errorf("state 1: expected AI provider chip, got:\n%s", html)
}
noActionButton(t, html, "summarized")
if strings.Contains(html, "Summarize now") {
if strings.Contains(html, "Summarize") {
t.Errorf("state 1: no nudge button on a summarized card, got:\n%s", html)
}
}
@@ -64,7 +64,7 @@ func TestVideoCard_State2_NoTranscript(t *testing.T) {
t.Errorf("state 2: expected 'No transcript available' text, got:\n%s", html)
}
noActionButton(t, html, "none-transcript")
if strings.Contains(html, "Summarize now") {
if strings.Contains(html, "Summarize") {
t.Errorf("state 2: no nudge button when there are no captions, got:\n%s", html)
}
}
@@ -84,7 +84,7 @@ func TestVideoCard_State3_Queued(t *testing.T) {
noActionButton(t, html, "queued")
}
// TestVideoCard_State4_RateLimited — quiet status + "Summarize now" → retry-now URL.
// TestVideoCard_State4_RateLimited — quiet status + "Summarize" → retry-now URL.
func TestVideoCard_State4_RateLimited(t *testing.T) {
html := renderVideoCard(t, store.SummaryRow{
VideoID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
@@ -93,11 +93,11 @@ func TestVideoCard_State4_RateLimited(t *testing.T) {
TranscriptStatus: "rate_limited",
})
if !strings.Contains(html, "Fetching soon") {
t.Errorf("state 4: expected 'Fetching soon' status text, got:\n%s", html)
if !strings.Contains(html, "In queue") {
t.Errorf("state 4: expected 'In queue' status text, got:\n%s", html)
}
if !strings.Contains(html, "Summarize now") {
t.Errorf("state 4: expected 'Summarize now' button, got:\n%s", html)
if !strings.Contains(html, "Summarize") {
t.Errorf("state 4: expected 'Summarize' button, got:\n%s", html)
}
if !strings.Contains(html, "retry-now") {
t.Errorf("state 4: expected retry-now URL in form action, got:\n%s", html)
@@ -110,7 +110,7 @@ func TestVideoCard_State4_RateLimited(t *testing.T) {
}
}
// TestVideoCard_State5_Pending — quiet status + "Summarize now" → summarize URL.
// TestVideoCard_State5_Pending — quiet status + "Summarize" → summarize URL.
func TestVideoCard_State5_Pending(t *testing.T) {
html := renderVideoCard(t, store.SummaryRow{
VideoID: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
@@ -121,8 +121,8 @@ func TestVideoCard_State5_Pending(t *testing.T) {
if !strings.Contains(html, "Not summarized") {
t.Errorf("state 5: expected 'Not summarized' status text, got:\n%s", html)
}
if !strings.Contains(html, "Summarize now") {
t.Errorf("state 5: expected 'Summarize now' button, got:\n%s", html)
if !strings.Contains(html, "Summarize") {
t.Errorf("state 5: expected 'Summarize' button, got:\n%s", html)
}
if !strings.Contains(html, "/summarize") {
t.Errorf("state 5: expected summarize URL in form action, got:\n%s", html)