From cca0a7461b67393148bd8e6d827f47e0da4af9f9 Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 1 Oct 2026 18:43:14 +0200 Subject: [PATCH] feat(web): tell the user when a video track was ignored, in every state A Teams meeting with the camera off is still a video file. It is accepted, only its audio is transcribed, and the page says so in queued, transcribing, done and failed -- not just at the end. Co-Authored-By: Claude Sonnet 5.5 --- internal/web/uploads.templ | 15 +++++ internal/web/uploads_templ.go | 117 ++++++++++++++++++++++++---------- 2 files changed, 100 insertions(+), 32 deletions(-) diff --git a/internal/web/uploads.templ b/internal/web/uploads.templ index b084c10..f4bf11b 100644 --- a/internal/web/uploads.templ +++ b/internal/web/uploads.templ @@ -60,6 +60,7 @@ templ uploadStatus(u domain.UploadedTranscript, chatEnabled bool) { switch u.Status { case domain.UploadDone:
+ @videoNotice(u.HadVideo)

Transcribed { uploads.FormatDuration(time.Duration(u.AudioSeconds * float64(time.Second))) } of audio by { u.Endpoint }. @@ -74,6 +75,7 @@ templ uploadStatus(u domain.UploadedTranscript, chatEnabled bool) {

case domain.UploadFailed: default: @@ -84,6 +86,7 @@ templ uploadStatus(u domain.UploadedTranscript, chatEnabled bool) { hx-trigger={ "every " + uploadPollEvery } hx-swap="outerHTML" > + @videoNotice(u.HadVideo) if u.Status == domain.UploadTranscribing {

Transcribing… a long recording takes a few minutes.

} else { @@ -108,3 +111,15 @@ templ UploadChatPage(u domain.UploadedTranscript, v chatView) { } } + +// videoNotice tells the user a video track was present and what became of it. A +// Teams meeting with the camera off is still a video file; it is accepted, only the +// audio is transcribed, and the picture is thrown away — so say so, in every state, +// instead of leaving the user to wonder (ADR-033). +templ videoNotice(hadVideo bool) { + if hadVideo { +

+ This file contained a video track. Only its audio was used; the video was discarded and nothing from it was kept. +

+ } +} diff --git a/internal/web/uploads_templ.go b/internal/web/uploads_templ.go index c848d97..396b7f2 100644 --- a/internal/web/uploads_templ.go +++ b/internal/web/uploads_templ.go @@ -5,10 +5,8 @@ package web //lint:file-ignore SA4006 This context is only used if a nested component is present. -import ( - "github.com/a-h/templ" - templruntime "github.com/a-h/templ/runtime" -) +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" import ( "fmt" @@ -268,133 +266,153 @@ func uploadStatus(u domain.UploadedTranscript, chatEnabled bool) templ.Component ctx = templ.ClearChildren(ctx) switch u.Status { case domain.UploadDone: - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "

Transcribed ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = videoNotice(u.HadVideo).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

Transcribed ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(uploads.FormatDuration(time.Duration(u.AudioSeconds * float64(time.Second)))) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 64, Col: 95} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 65, Col: 95} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " of audio by ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " of audio by ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(u.Endpoint) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 65, Col: 48} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 66, Col: 48} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, ".

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

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

Ask about this recording

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">Ask about this recording

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "
Transcript
")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
Transcript
")
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
 			var templ_7745c5c3_Var16 string
 			templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(u.Content)
 			if templ_7745c5c3_Err != nil {
-				return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 72, Col: 40}
+				return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 73, Col: 40}
 			}
 			_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } case domain.UploadFailed: - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "

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

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = videoNotice(u.HadVideo).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(uploadFailureMessage(u.ErrorKind)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 77, Col: 42} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 79, Col: 42} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "

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

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } default: - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "\" hx-swap=\"outerHTML\">") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = videoNotice(u.HadVideo).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if u.Status == domain.UploadTranscribing { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "

Transcribing… a long recording takes a few minutes.

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

Transcribing… a long recording takes a few minutes.

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

Queued — waiting for its turn to be transcribed.

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

Queued — waiting for its turn to be transcribed.

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -438,20 +456,20 @@ func UploadChatPage(u domain.UploadedTranscript, v chatView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "

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

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var22 string templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(u.Title) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 101, Col: 16} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/web/uploads.templ`, Line: 104, Col: 16} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "

Ask about this recording

Answers come only from this recording's transcript, which is private to you.

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

Ask about this recording

Answers come only from this recording's transcript, which is private to you.

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -459,20 +477,20 @@ func UploadChatPage(u domain.UploadedTranscript, v chatView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "

Back to the transcript

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "\">Back to the transcript

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -486,4 +504,39 @@ func UploadChatPage(u domain.UploadedTranscript, v chatView) templ.Component { }) } +// videoNotice tells the user a video track was present and what became of it. A +// Teams meeting with the camera off is still a video file; it is accepted, only the +// audio is transcribed, and the picture is thrown away — so say so, in every state, +// instead of leaving the user to wonder (ADR-033). +func videoNotice(hadVideo 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 { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var24 := templ.GetChildren(ctx) + if templ_7745c5c3_Var24 == nil { + templ_7745c5c3_Var24 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + if hadVideo { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "

This file contained a video track. Only its audio was used; the video was discarded and nothing from it was kept.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + return nil + }) +} + var _ = templruntime.GeneratedTemplate