diff --git a/app/msgviewer.go b/app/msgviewer.go index d0f771a..e62aae5 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -634,16 +634,16 @@ func (pv *PartViewer) attemptCopy() { cleaned, images := parse.ExtractImages(&filterBuf) if len(images) > 0 { pv.imageRefs = images - cleanedBytes, _ := io.ReadAll(cleaned) - pv.composite = newCompositeContent( - string(cleanedBytes), images, 80) - pv.Invalidate() - } else { - // No images — forward to pager as normal - _, copyErr := io.Copy(pv.pagerin, cleaned) - if copyErr != nil { - log.Errorf("io.Copy: %s", copyErr) - } + log.Debugf("extracted %d image refs from filter output", len(images)) + } + // Always forward to pager — placeholders are readable text + // Image markers become \x00IMG:N\x00 which we replace + // with human-readable [image: alt] text for the pager. + cleanedBytes, _ := io.ReadAll(cleaned) + output := parse.ReplacePlaceholders(cleanedBytes, images) + _, copyErr := io.Copy(pv.pagerin, bytes.NewReader(output)) + if copyErr != nil { + log.Errorf("io.Copy: %s", copyErr) } err = pv.pagerin.Close() if err != nil { @@ -838,13 +838,9 @@ func (pv *PartViewer) Draw(ctx *ui.Context) { if pv.term != nil { pv.term.Draw(ctx) } - // Composite mode: text + images from filter output - if pv.composite != nil { - pv.composite.width = ctx.Width() - ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', pv.uiConfig.GetStyle(config.STYLE_DEFAULT)) - pv.composite.Draw(ctx, pv.scroll) - return - } + // NOTE: Composite image rendering (pv.composite) is reserved for + // future use. Currently all filter output goes through the pager + // with [image: alt] text placeholders for extracted images. if pv.image != nil && (pv.resized(ctx) || pv.graphic == nil) { // This path should only occur on resizes or the first pass // after the image is downloaded and could be slow due to diff --git a/lib/parse/extract_images.go b/lib/parse/extract_images.go index 9db1774..74b147b 100644 --- a/lib/parse/extract_images.go +++ b/lib/parse/extract_images.go @@ -39,3 +39,33 @@ func ExtractImages(r io.Reader) (io.Reader, []ImageRef) { } return buf, images } + +// ReplacePlaceholders converts \x00IMG:N\x00 placeholders into human-readable +// [image: alt] text suitable for display in a pager. +func ReplacePlaceholders(data []byte, images []ImageRef) []byte { + result := bytes.NewBuffer(nil) + for _, line := range bytes.Split(data, []byte("\n")) { + trimmed := bytes.TrimSpace(line) + if len(trimmed) > 6 && trimmed[0] == 0 && + bytes.HasPrefix(trimmed, []byte("\x00IMG:")) && + trimmed[len(trimmed)-1] == 0 { + // Extract index + numStr := string(trimmed[5 : len(trimmed)-1]) + idx := 0 + for _, c := range numStr { + if c >= '0' && c <= '9' { + idx = idx*10 + int(c-'0') + } + } + if idx < len(images) && images[idx].Alt != "" { + fmt.Fprintf(result, "[image: %s]\n", images[idx].Alt) + } else { + result.WriteString("[image]\n") + } + } else { + result.Write(line) + result.WriteByte('\n') + } + } + return result.Bytes() +}